Looking inside Go - Reverse Engineering
Tags: security, cyber, ctf, hacking
Published: 2020-07-18 11:22:48

# First: the Context
Google hosts an abundance of technical events where avid programmers and engineers can get hands-on experience solving problems and battling it out against some of the best engineers in the world. One event in particular is the Google CTF where cybersec experts can try their hand at hacking at various levels of security challenges.

Google also host a beginner's quest (the "qualifiers") for those looking for a challenge but are just getting started in the industry. While it is called the "beginner's quest" the challenges are usually by no means simple for those just starting out. However, they require less man power and breadth of knowledge compared to the main event. 

# Beginner's Quest 2019
One of the challenges from the 2019 quest was simply named "Satellite" and labelled as a networking challenge.

![alt text](https://i.imgur.com/ZiqweoM.png "The satellite challenge")

The linked attachment is a compressed file which contains a pdf and an unknown file named `init_sat`.

![alt text](https://i.imgur.com/hz3wzNr.png "List of files on command line")

We can use the `file` command to get a better idea of what the `init_sat` file is.

```bash
$ file init_sat 
init_sat: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, Go BuildID=YhfyV09rKV_0ewkLiNr1/6ZJO5J8awFQSRgZDzlnA/zvyuoO7Qu3ralSU_Aheb/QK0rATh0jzljJY8j2313, not stripped
```
Here we can see that it is actually a 64-bit executable. We can also see that the binary is not stripped, indicating that the symbols table is still intact. This is useful as it means the binary contains the function names as well as other useful debug information.

# Running, Running, Running
To get an idea of what we are dealing with we should first execute the binary. After allowing execution (`chmod +x`) we see that the program outputs a prompt asking for the name of the satellite.

![alt text](https://i.imgur.com/qJCtIEI.png "Running the binary")

# Practise what you preach 
It's always good practice to try what we know before digging deep. When presented with user input we can test for the usual pitfalls; buffer overflows, [printf formatting bugs](https://owasp.org/www-community/attacks/Format_string_attack) and so on. Unfortunately these trivial tests proved futile in revealing any of the typical bugs.

# And Dumping...
To dig further, we need to dump the assembly code and understand how the program functions.
```bash
$ objdump -D init_sat > init_sat.asm
```
On inspection of the dump we notice there is a *lot* of assembly code. In fact the assembly dump is a total of 85MB of text!

![alt text](https://i.imgur.com/wajMLRv.png "Massive assembly code file")

This is a bit unexpected, especially since we noted before that the executable was dynamically linked. The point of a dynamically linked executable is that it uses shared libraries already installed on your operating system. In doing so it does not have to include bundled library code, greatly reducing its size.

Skimming through a small section of the dumped assembly, we notice a few functions that sound like standard library functions. For example, `x_cgo_sys_thread_create`. Searching for this function reveals that this binary was actually written in Go.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/nirj1n3nemulqyshiv8a.png "Search for the function name")

When a Go binary is compiled, the standard libaries/Go runtime are statically linked. This has the consequence of [ballooning the overall file size](https://golang.org/doc/faq#Why_is_my_trivial_program_such_a_large_binary), resulting in even basic CLI programs being megabytes in size.

Fortunately Go also includes a powerful toolkit that we can use to debug and investigate further. For example, it includes an `objdump` command which we can use to dump the main function.

```bash
$ go tool objdump -s "main.main" init_sat
```
This command outputs way more debug information, with Go-specific symbols being included in the output. The dump also includes the Go file and associated line number that the assembly implements. Here is a snippet to illustrate what that looks like.

```asm
  init_sat.go:12    MOVQ CX, 0xb0(SP)
  init_sat.go:12    NOPL
  print.go:243      MOVQ os.Stdout(SB), CX
  print.go:243      LEAQ go.itab.*os.File,io.Writer(SB), DX
  print.go:243      MOVQ DX, 0(SP)
  print.go:243      MOVQ CX, 0x8(SP)
  print.go:243      LEAQ 0xa8(SP), CX
  print.go:243      MOVQ CX, 0x10(SP)
  print.go:243      MOVQ $0x1, 0x18(SP)
  print.go:243      MOVQ $0x1, 0x20(SP)
  print.go:243      CALL fmt.Fprint(SB)
  init_sat.go:16    NOPL
  init_sat.go:16    MOVQ os.Stdin(SB), AX
  init_sat.go:16    MOVQ AX, 0x70(SP)
```
_NB: Memory addresses have been ommitted for ease of reading_

Here we can see assembly code for the main file (`init_sat.go`) and one of the standard library functions (`print.go`). This clear distinction gives us more power to glance over the assembly and get to what we really care about. Since we aren't dissecting the standard library, we can mostly ignore the assembly code from anything other than `init_sat.go`.

How that we have a clear focus we can start to cross-reference the behaviour of the program with the assembly in front of us.

Recalling the functionality, we note that the program first prints a message to the user. This takes form in the assembly snippet above with the call to `fmt.Fprint`.

The user is then prompted to enter the name of the satellite to begin the connection. It achieves this via the `bufio.go` library code which makes a call to `bufio.(*Reader).ReadBytes`.

```asm
  print.go:243     CALL fmt.Fprint(SB)
  init_sat.go:18   NOPL
  bufio.go:474     LEAQ 0x110(SP), AX
  bufio.go:474     MOVQ AX, 0(SP)
  bufio.go:474     MOVB $0xa, 0x8(SP)
  bufio.go:474     CALL bufio.(*Reader).ReadBytes(SB)
```
We can continue to skim over this library code until we find assembly from `init_sat.go` again.

```asm
  bufio.go:475      0x4f8b87    MOVQ CX, 0x68(SP)			
  init_sat.go:20    0x4f8b8c    MOVQ CX, 0(SP)				
  init_sat.go:20    0x4f8b90    MOVQ AX, 0x8(SP)			
  init_sat.go:20    0x4f8b95    CALL strings.ToLower(SB)
  init_sat.go:20    0x4f8b9a    MOVQ 0x10(SP), AX
  init_sat.go:20    0x4f8b9f    MOVQ 0x18(SP), CX
```

The `strings.ToLower` function is part of the [Go standard library](https://golang.org/pkg/strings/#ToLower). As the name suggests, it converts a string to lowercase and returns the resulting string.

We can assume that one of the `MOVQ` instructions is moving the lowercase string into a register. We can continue to follow the program flow to figure out whether it is the `AX` or `CX` register.

```asm
  init_sat.go:24    0x4f8ba4    CMPQ $0x5, CX
  init_sat.go:24    0x4f8ba8    JNE 0x4f8bbc	
```

This code block compares `0x5` to the value in the `CX` register and then jumps to a memory address if it is not equal. For now we can assume that `CX == 5` and follow that branch of execution. In this case the program continues directly after the `JNE` instruction.

```asm
  init_sat.go:24    0x4f8baa    CMPL $0x74697865, 0(AX)
  init_sat.go:24    0x4f8bb0    JNE 0x4f8bbc				
  init_sat.go:24    0x4f8bb2    CMPB $0xa, 0x4(AX)	
  init_sat.go:24    0x4f8bb6    JE 0x4f8ca6
```
The instructions above compare multiple values with the contents of the `AX` register. The first instruction compares `0x74697865` to `AX` followed by a comparison of `0xa` with `AX` offset by 0x4 bytes. 

Converting the literal values (represented in hexadecimal) to ASCII, we note that they translate to 'tixe' and the newline character. Reversing the text (due to LSB format) reveals the string 'exit\n'.

![alt text](https://i.imgur.com/qJCtIEI.png "Running the binary")

Obviously these instructions are checking if the user entered 'exit' to quit. We can then conclude that the `JE` instruction will jump to a memory address which prints "Exiting, goodbye" before terminating the program.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/jpg0pmad3c2qwsve02k4.png "Print goodbye string before exiting")

This is interesting, but not exactly what we are after. We need to know what name for the satellite the program is expecting. Let's take a look where the execution jumps to when `CX != 5`.

```asm
  init_sat.go:21    0x4f8bbc    CMPQ $0x7, CX
  init_sat.go:21    0x4f8bc0    JNE 0x4f8bdc				
  init_sat.go:21    0x4f8bc2    CMPL $0x696d736f, 0(AX)
  init_sat.go:21    0x4f8bc8    JNE 0x4f8bdc				
  init_sat.go:21    0x4f8bca    CMPW $0x6d75, 0x4(AX)
  init_sat.go:21    0x4f8bd0    JNE 0x4f8bdc				
  init_sat.go:21    0x4f8bd2    CMPB $0xa, 0x6(AX)
  init_sat.go:21    0x4f8bd6    JE 0x4f8c89
```
At this point we can probably infer that the `CX` register is storing the length of the entered string. Previously the `CMPQ` instruction checked for `0x5` and the instructions that followed were looking for 'exit\n' - a five byte string. The first instruction in this code block is comparing `0x7` to the `CX` register, implying that the satellite name is seven characters in length.

Again, the instructions execute a string comparison. The first `CMPL` compares 0x696d736f, or 'imso' with `AX`. If this passes, it then compares 0x6d75 ('mu') with `AX` offset `0x4` bytes. The final comparison checks for the familiar newline character. Reversing and putting it altogether we get `osmium\n` - a seven character string!

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/eu9dk7a8nfsngcpik8s4.png "Successfully connecting to the satellite")

# Sniffing out the flag
After connecting to the satellite we are prompted with several options. The first option is of particular interest.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/bq8n6pvgvuhbb1zdb3va.png "Displaying config data")

Here we can see the `username` and `password` printed to the screen. Unfortunately the `password` has been redacted. The config data response also includes a link to a Google doc which has a single base64 encoded string.

```bash
$echo "VXNlcm5hbWU6IHdpcmVzaGFyay1yb2NrcwpQYXNzd29yZDogc3RhcnQtc25pZmZpbmchCg==" | base64 -d
Username: wireshark-rocks
Password: start-sniffing!
```
As suggested let's fire up wireshark and start sniffing! Once we setup wireshark to sniff the correct interface, we need to reconnect to the satellite to capture the packets that send the config data.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/2rzeeb3rmzcdthqj60wr.png "Filtered TCP capture")

To filter the noise, we can search the packet bytes for the "Username" string, as shown in the screenshot above. We can then follow the TCP stream to reveal the plaintext password.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/01tm66ju3zr315mnbpo3.png "TCP stream revealing password")

That's it! We have successfully solved this CTF challenge by first reversing the Go binary to find the satellite name, connecting to the satellite and finally sniffing for the password which was returned in plaintext over the wire.