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.
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 *ptr; // [rsp+420h] [rbp-20h] Item *item; // [rsp+428h] [rbp-18h] Item 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 ) { = find_empty_item(items); empty_item if ( empty_item != -1 ) { = getenv("SECRET"); secret = (Item *)malloc(0x10uLL); item ->name = strdup(secret); item->cost = 1290; item->amount = 331; item->items[empty_item] = item; items} } (fd, items); print_cart->sum_cost = 0LL; items->MUST_BE_NON_ZERO = 0LL; itemsfor ( i = 0; i <= 31; ++i ) { = items->items[i]; ptr if ( ptr ) { (ptr->name); free(ptr); free->items[i] = 0LL; items} } (command, "rm -rf %s", session); sprintfreturn 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
:void __fastcall change_amount(int fd, Cart *items, const char *session) // 4021EC { char item_name[256]; // [rsp+20h] [rbp-120h] BYREF Item *ptr; // [rsp+120h] [rbp-20h] int item_or_empty; // [rsp+12Ch] [rbp-14h] char *path; // [rsp+130h] [rbp-10h] int path_length; // [rsp+13Ch] [rbp-4h] send(fd, "Enter item name: ", 0x11uLL, 0); read_line(fd, item_name, 128); path_length = snprintf(0LL, 0LL, "%s/%s", session, item_name); path = (char *)malloc(path_length + 1); sprintf(path, "%s/%s", session, item_name); if ( contains_item(items, item_name) ) { item_or_empty = find_item_or_empty(items, item_name, path); (ref:critical) if ( item_or_empty == -1 ) send(fd, "No such item!\n", 0xEuLL, 0); ptr = items->items[item_or_empty]; if ( ptr ) { free(ptr->name); free(ptr); } ptr = input_item(fd, item_name); items->items[item_or_empty] = ptr; } free(path); }
find_item_or_empty
:(Cart *items, const char *item_name, const char *path) // 401DDF __int64 __fastcall find_item_or_empty{ unsigned int empty_item; // [rsp+2Ch] [rbp-14h] *v6; // [rsp+30h] [rbp-10h] Item int i; // [rsp+38h] [rbp-8h] unsigned int v8; // [rsp+3Ch] [rbp-4h] = -1; v8 for ( i = 0; i <= 31; ++i ) { = items->items[i]; v6 if ( v6 && !strcmp(v6->name, item_name) ) { = i; v8 break; } } if ( access(path, 0) == -1 ) return 0xFFFFFFFELL; // -2 if ( v8 != -1 ) return v8; = find_empty_item(items); empty_item if ( empty_item == -1 ) return 0xFFFFFFFFLL; // -1 else return empty_item; }
contains_item
:int __fastcall contains_item(Cart *items, const char *item_name) { *v3; // [rsp+10h] [rbp-10h] Item int i; // [rsp+1Ch] [rbp-4h] for ( i = 0; i <= 31; ++i ) { = items->items[i]; v3 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 (critical) 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.
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
)