Download challenges: Neuland CTF Repository
Author: Manu
The flag is hidden somewhere in this binary.
strings
The challenge is straightforward if you already know what strings
is. It is a program that display printable strings in files.
We can execute strings
on our binary strings
and we can find the flag in the output.
$ strings strings
/lib64/ld-linux-x86-64.so.2
__cxa_finalize
__libc_start_main
puts
libc.so.6
GLIBC_2.2.5
GLIBC_2.34
_ITM_deregisterTMCloneTable
__gmon_start__
_ITM_registerTMCloneTable
PTE1
u+UH
nland{f0H
und_y0u}H
Try to get the flag!
;*3$"
...
The flag is nland{f0und_y0u}
.
Author: Manu
Can you trace down the admin password? Strings won't help you this time.
Flag format: nland{admin-password}
tracer
We are asked to find the admin password. We can't use strings
this time as the binary contains a lot of strings. We have to find another tool that we can used for reverse engineering. The title and description hints us towards ltrace. We can use it to trace the library calls of a given program.
$ ltrace ./tracer
printf("Enter admin password: ") = 22
__isoc99_scanf(0x55cd2f78d01b, 0x7ffc5a4f50f0, 0, 0Enter admin password: test
) = 1
strcmp("test", "42ceec6b744d41bc8044fee516003183"...) = 64
printf("Wrong password") = 14
Wrong password+++ exited (status 0) +++
Our input is compared with the string "42ceec6b744d41bc8044fee516003183" followed by the call to printf("Wrong password")
. This seems promising. We will try this again with the found string.
$ ltrace ./tracer
printf("Enter admin password: ") = 22
__isoc99_scanf(0x55ec8584001b, 0x7fff1f04d610, 0, 0Enter admin password: 42ceec6b744d41bc8044fee516003183
) = 1
strcmp("42ceec6b744d41bc8044fee516003183"..., "42ceec6b744d41bc8044fee516003183"...) = 0
printf("Right password") = 14
Right password+++ exited (status 0) +++
We found the right password.
The flag is nland{42ceec6b744d41bc8044fee516003183}
.
Author: Manu
I encoded the flag with a custom script that I wrote. I lost the source code to it. I just found this odd file in the project folder. It seems to describe operations and commands in some way to encode the flag. Maybe this helps you to find a way to decode the flag.
This is the encoded flag: urfrg}qy6f-jZ.e-'U]((QSi&!POf
.
encode
The contents or the structure of the encode
file are probably unfamiliar for most people. After some research you will find out that this is Python bytecode. It describes your source code as a low-level platform-independent representation. The challenge description states that this script is used to encode the flag. After we get the hang of how Python bytecode looks we can recover the original encode funtion.
def encode(flag):
o = ''
for i, b in enumerate(flag):
b = ord(b)
b = b + 7 - i
a = chr(b)
o += a
return o
Now we know how the flag was encoded. However, we need to decode it. We are going to reverse the encode function to get the decode function. We end up with something like this.
def decode(flag):
f = ''
for i, b in enumerate(flag):
c = ord(b)
c = c - 7 + i
c = chr(c)
f += c
return f
If we input the encoded flag into our decode function, we can retrieve the flag.
The flag is nland{py7h0n_4l50_h45_by73c0d3}
.
Author: Kevin Don't Worry, Relax, Chill and Try harder
password
The program asks for a password. The challenge is to get the password through reversing the binary. There is some string stacking implemented within the binary.
The solution requires to reverse the binary and check which variables are used to fulfill the last if
. Another solution requires to check what the last print
prints. This is the flag, if the user provided the correct password. However, the printed variable (v4
) is stacked too, which requires some work to determine which strings are used to build the flag.
package main
import (
"bufio"
"fmt"
"os"
"strings"
"bytes"
)
func main() {
s1 := "nland{th1s_1s_d3f1n3tly_n0t_th3_fl4g"
s2 := "nland{w0w_y0u_f0und_"
s3 := "th3_fl4g!}"
s4 := "nland{"
s5 := "s0_m3ny_"
s6 := "4w3s0m3_fl4gs_"
s7 := "1n_th1s_b1n4ry}"
s8 := "nland{1t_1s_"
s9 := "th3_fl4g_"
s10 := "1_w4s_"
s11 := "l00k1ng_for!}"
var b bytes.Buffer
b.WriteString(s4)
b.WriteString(s5)
b.WriteString(s6)
b.WriteString(s7)
var sb strings.Builder
sb.WriteString(s8)
sb.WriteString(s9)
sb.WriteString(s10)
sb.WriteString(s11)
fmt.Println("Welcome to NEULAND CTF!")
fmt.Println("Please enter the password:")
inp, _, err := bufio.NewReader(os.Stdin).ReadLine()
if err != nil {
fmt.Println("Uhm, something went wrong!", err)
fmt.Println("nland{th1s_1s_d3f1n3tly_n0t_th3_fl4g")
}
v1 := s1
_ = v1
v2 := fmt.Sprintf("%s%s", s2, s3)
_ = v2
v3 := b.String()
_ = v3
v4 := sb.String()
_ = v4
if string(inp) == s6 + s9 + s11 {
fmt.Println("You got it!")
fmt.Println(v4)
} else {
fmt.Println("Don't Worry, Relax, Chill and Try harder")
}
}
The password is: 4w3s0m3_fl4gs_th3_fl4g_l00k1ng_for!}
Flag: nland{1t_1s_th3_fl4g_1_w4s_l00k1ng_for!}
Author: Dominik The early bird catches the baby.
Insert description
This challenge was marked as hard. We get the binary, and a server address to
connect to. Connecting to the server with f.e. netcat (1)
greets us with the
following prompt:
integrity: f3ea30e1f4af98a5b805070ce0e8bff50749958bdfcc51047eae911508573f40395a219636daef20d913bb0548b7baef7487beaa035973dab78fbddce61a7e58
Enter password:
🔒
If this was a pwn-challenge, this would look like something where we have to
trick the integrity check. Alas, this is reversing, so let's have a look at the
provided earlybird
file:
$ file earlybird
earlybird: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, not stripped
So, the provided file is a binary file. Since we are security professionals, the first thing we do, is run the application we just downloaded from the internet:
$ ./earlybird
integrity: f3ea30e1f4af98a5b805070ce0e8bff50749958bdfcc51047eae911508573f40395a219636daef20d913bb0548b7baef7487beaa035973dab78fbddce61a7e58
Enter password:
🔒 foobar
💣 Nice try!
OK, so this is interesting. Looks like the server is running the same binary and we need to input some password. Either the binary is identical (same integrity tag), or the author is intentionally screwing with us. Let's assume the first option and start reversing!
We are going to use Ghidra for that. After loading
the binary, and running a initial auto-analysis, we are presented with the
disassembly listing, and the exported symbols in the "Exports"-view on the left.
Ghidra automatically navigates us to the main()
-function which is obviously
the entry point of the application. We also get a decompiler listing, which is
much easier to read than the raw assembly:
This looks pretty straight forward:
init()
-function, which does some weird iobuf
-stuff
(author's note: this makes the application run well with socat)checksum()
-function, that prints the ominous checksum line that we saw in
the netcat-outputprintf()
, that prompts for the password, which is then read by
fgets
strcspn
(very exotic :-))FUN_00401973
function. If they are equal, we get flag, else is prints the bomb
icon.Well, easy. We just have to figure out, what this FUN_00401973
does to the
input string ysae_oot_yaw
:
We can either analyze this manually, dynamically or by using a clever technique
called FunctionID.
Turns out, this ia a strrev
-function, which just reverses the input string.
Therefore, ysae_oot_yaw
turns to way_too_easy
- indeed, that was way too easy :-).
Let's use the password and collect the flag:
$ ./earlybird
integrity: f3ea30e1f4af98a5b805070ce0e8bff50749958bdfcc51047eae911508573f40395a219636daef20d913bb0548b7baef7487beaa035973dab78fbddce61a7e58
Enter password:
🔒 way_too_easy
💣 Nice try!
😱
It didn't work. But why? We did everything correctly. We can even verify it in the debugger!
The strcmp
-call is obviously correct and we even get the dummyflag. Wait,
what? We didn't get the flag before. What is happening?
This is where the challenge gets interesting. It behaves differently, when we run it in the debugger. There are two options here: either you are familiar with anti-debugging techniques and spotted it immediately, or you should freshen up your tipps & tricks on malware reversing ;-)
The guide (Schallner, M. "Beginners guide to basic linux anti anti debugging techniques." Code Breakers Magazine 1 (2006)) mentions something about ptrace
in chapter 4, "Detecting debugging".
Let's test our theory, by tracing the syscalls:
$ strace -- ./earlybird
execve("./earlybird", ["./earlybird"], 0x7ffc8f2d6118 /* 41 vars */) = 0
[...]
ptrace(PTRACE_TRACEME) = -1 EPERM (Operation not permitted)
[...]
With our impressive deduction skills, we have finally caught the criminal. There
is something fishy going on, where the application somehow detects the attached
debugger and modifies it's behaviour. There are multiple options here:
ptrace
-syscall is and try to
figure out our way from there For the sake of this writeup, let's go with the second option. Therefore,
Gentlemen, start your
enginesgdb.
$ gdb -q earlybird
> catch syscall ptrace
Catchpoint 1 (syscall 'ptrace' [101])
> run
Catchpoint 1 (call to syscall ptrace), 0x000000000045352e in ?? ()
> set $rax = 0
> b *(main+118)
Breakpoint 2 at 0x401a43
> continue
Continuing.
integrity: f3ea30e1f4af98a5b805070ce0e8bff50749958bdfcc51047eae911508573f40395a219636daef20d913bb0548b7baef7487beaa035973dab78fbddce61a7e58
Enter password:
🔒 foobar
Breakpoint 2, 0x0000000000401a43 in main ()
And suddenly, the password changed to crt0_trickzz
? Validating this locally
confirms that this is the password when running without the debugger.
Since we are very curious, we want to figure out, how this works. Luckily, gdb
already gave us the return address for the ptrace-syscall, which is 0x45352e
.
Jumping to this address in Ghidra, we find a somewhat weird function that issues
the syscall. This is the libc interface (ptrace
) to the corresponding syscall,
so we need to go deeper and get the calling functions. Ghidra presents those via
the Xrefs, and indeed, following the first (and only) code Xref, we find a
function at address 0x401755
.
Ghidra presents us with the following decompiler listing:
The key takeaways here:
ptrace
-stub, and only runs when
there is no error, i.e. there was no debugger attachedFUN_00425430
, this is memfrob(3)
) a data block
starting at 0x4c70e0 + 0x2f20 = 0x4CA000
. way_too_easy
)
gets patched by a code stub that is injected in-memory.crt0_trickzz
.
Finally, we can use this alternative password to retrieve the flag from the
server. The flag is nland{c47ch35_7h3_w02m}
.
There is one question left: Why did we not see the ptrace
call in the main
disassembly? The answer to this is hinted in the modified password: the
application uses a special trick, where it runs the modification code before
even the main function, by hooking into the crt0 startup routine. This can also
be confirmed by checking the Xrefs to the _entry
and 0x401755
-function.
This is left as an exercise to the interested reader.