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 and Cart

    We'll get to the MUST_BE_NON_ZERO field later

  • Take 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 type Proceed . Mention that Cart *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|
    2|
    3|
    4|
    5|
    6|
    7|
    8|
    9|
    10|
    11|
    12|
    13|
    14|
    15|
    *16|
    17|
    18|
    19|
    20|
    21|
    22|
    23|
    24|
    25|
    26|
    27|
    28|
    29|
    
    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);
    		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 :

    __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, but find_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

  1. connect to service, create session(1 ) and then create item(1 )
  2. connect to service, join the session(2 ) created in 1. , reload cart(3 ) and then delete this item(2 )
  3. in connection 1. change item(5 ) whaterever you want and then proceed(7 )
Made with Org mode, © 2023 - ∞ iliayar