Student CTF -> Shop
Table of Contents
I’ve created a custom shopping cart system. Can you get the flag?
nc 164.90.193.27 50009
shop.elf
We get the binary shop_8059c17ac3.elf
so lets open up decompiler and
play with it a bit.
1 Analysis
The programm opens port 50009
and wait for a connection:
$ nc localhost 50009 Welcome to our password storage! 1> Create New Session 2> Work With Existing Session 3> Exit 1 90495438b91bc707910561255b74c94c 1> Add new item to shopping cart 2> Delete item from shopping cart 3> Reload shopping cart 4> Print shopping cart 5> Change amount 6> Enter coupon to get gift 7> Proceed 8> Logout 1 Enter item name: My Item Enter item cost: 10 Enter item amount: 1 1> Add new item to shopping cart 2> Delete item from shopping cart 3> Reload shopping cart 4> Print shopping cart 5> Change amount 6> Enter coupon to get gift 7> Proceed 8> Logout 4 Total: 10$ Item: Name: My Item Price: 10$ Amount: 1 1> Add new item to shopping cart 2> Delete item from shopping cart 3> Reload shopping cart 4> Print shopping cart 5> Change amount 6> Enter coupon to get gift 7> Proceed 8> Logout
What I've got during one hour:
Let's define the 2 structures program use for convenience:
Item
andCart
We'll get to the
MUST_BE_NON_ZERO
field laterTake a closer look on function for variant
7> Proceed
of the second menu.int __fastcall proceed(unsigned int fd, Cart *items, const char *session) // 4025C4 { char command[1024]; // [rsp+20h] [rbp-420h] BYREF Item *ptr; // [rsp+420h] [rbp-20h] Item *item; // [rsp+428h] [rbp-18h] char *secret; // [rsp+430h] [rbp-10h] int empty_item; // [rsp+438h] [rbp-8h] int i; // [rsp+43Ch] [rbp-4h] if ( items->MUST_BE_NON_ZERO ) { empty_item = find_empty_item(items); if ( empty_item != -1 ) { secret = getenv("SECRET"); item = (Item *)malloc(0x10uLL); item->name = strdup(secret); item->cost = 1290; item->amount = 331; items->items[empty_item] = item; } } print_cart(fd, items); items->sum_cost = 0LL; items->MUST_BE_NON_ZERO = 0LL; for ( i = 0; i <= 31; ++i ) { ptr = items->items[i]; if ( ptr ) { free(ptr->name); free(ptr); items->items[i] = 0LL; } } sprintf(command, "rm -rf %s", session); return system(command); }
int __fastcall find_empty_item(Cart *a1) { int i; // [rsp+14h] [rbp-4h] for ( i = 0; i <= 31; ++i ) { if ( !a1->items[i] ) return i; } return 4294967295; // -1 }
So obviously we need to set
MUST_BE_NON_ZERO
to non zero value and then typeProceed
. Mention thatCart *items
is created when client establish connection, and all operations will be performed precisely with this instsance.The function corresponding to
5> Change amount
:1: void __fastcall change_amount(int fd, Cart *items, const char *session) // 4021EC 2: { 3: char item_name[256]; // [rsp+20h] [rbp-120h] BYREF 4: Item *ptr; // [rsp+120h] [rbp-20h] 5: int item_or_empty; // [rsp+12Ch] [rbp-14h] 6: char *path; // [rsp+130h] [rbp-10h] 7: int path_length; // [rsp+13Ch] [rbp-4h] 8: 9: send(fd, "Enter item name: ", 0x11uLL, 0); 10: read_line(fd, item_name, 128); 11: path_length = snprintf(0LL, 0LL, "%s/%s", session, item_name); 12: path = (char *)malloc(path_length + 1); 13: sprintf(path, "%s/%s", session, item_name); 14: if ( contains_item(items, item_name) ) 15: { 16: item_or_empty = find_item_or_empty(items, item_name, path); 17: if ( item_or_empty == -1 ) 18: send(fd, "No such item!\n", 0xEuLL, 0); 19: ptr = items->items[item_or_empty]; 20: if ( ptr ) 21: { 22: free(ptr->name); 23: free(ptr); 24: } 25: ptr = input_item(fd, item_name); 26: items->items[item_or_empty] = ptr; 27: } 28: free(path); 29: }
find_item_or_empty
:__int64 __fastcall find_item_or_empty(Cart *items, const char *item_name, const char *path) // 401DDF { unsigned int empty_item; // [rsp+2Ch] [rbp-14h] Item *v6; // [rsp+30h] [rbp-10h] int i; // [rsp+38h] [rbp-8h] unsigned int v8; // [rsp+3Ch] [rbp-4h] v8 = -1; for ( i = 0; i <= 31; ++i ) { v6 = items->items[i]; if ( v6 && !strcmp(v6->name, item_name) ) { v8 = i; break; } } if ( access(path, 0) == -1 ) return 0xFFFFFFFELL; // -2 if ( v8 != -1 ) return v8; empty_item = find_empty_item(items); if ( empty_item == -1 ) return 0xFFFFFFFFLL; // -1 else return empty_item; }
contains_item
:int __fastcall contains_item(Cart *items, const char *item_name) { Item *v3; // [rsp+10h] [rbp-10h] int i; // [rsp+1Ch] [rbp-4h] for ( i = 0; i <= 31; ++i ) { v3 = items->items[i]; if ( v3 && !strcmp(v3->name, item_name) ) return 1; } return 0; }
The
contains_item
function only checks if the items is presented in the cart, butfind_item_or_empty
try to access the corresponding file, and if it's, for example, deleted returns-2
.
So now we just want the call to find_item_or_empty
on 16 line
in change_amount
to return -2
, and the items->items[-2]
is exactly
NEED_TO_BE_NON_ZERO
field, which then sets to non zero value.
2 Exploitation
- connect to service, create session(
1
) and then create item(1
) - connect to service, join the session(
2
) created in 1., reload cart(3
) and then delete this item(2
) - in connection 1. change item(
5
) whaterever you want and then proceed(7
)