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

    ida_struct1.png2021-10-19-003219_475x100_scrot.png ida_struct2.png2021-10-19-003301_547x128_scrot.png

    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: 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, 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 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

  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)

Home Page