我们是由Eur3kA和flappypig组成的联合战队r3kapig。本周末,我们参与了趋势科技举办的TrendMicro CTF 2018 Qualifier 并以第十名的成绩成功晋级12月在日本东京举办的TrendMicro CTF 2018 Final。我们决定把我们做出来的题目的writeup发出来分享给大家。
另外我们战队目前正在招募队员,欢迎想与我们一起玩的同学加入我们,尤其是Misc/Crypto的大佬,有意向的同学请联系lgcpku@gmail.com。给大佬们递茶。
由于是国际比赛,所以我们的首发wp为英文版,中文版正在路上~
Analysis-Offense
200
I just modified my callgrind solver to solve this challenge.
$ cat oracle.py
#!/usr/bin/python -u
#-*- coding:utf-8 -*-
# Let's exploit easy and quick!
# 1) apt install valgrind
# 2) use callgrind to find instruction count
flag = 'TMCTF{'
n = 0
import os
import sys
# format given by admin
charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789{}"
while True:
n += 1
total_call_count = {}
for i in charset:
cmd = "valgrind --tool=callgrind --dump-instr=yes --callgrind-out-file=temp/call_count ./oracle '" + flag + i + "A' 2>&1"
# print(cmd)
res = os.popen(cmd).read()
call_count = res.split("Collected : ")[1].split()[0]
call_count = int(call_count)
# total_call_count { 'call_count': [occured_count, occured_by], ... }
if not total_call_count.get(call_count):
total_call_count[call_count] = [1, [i]]
else:
total_call_count[call_count][0] += 1
total_call_count[call_count][1].append(i)
print(n, i, call_count)
## get lowest/highest idx,
idx_call_count = total_call_count.keys()
print(idx_call_count)
idx_call_count.sort()
highest_count_idx = idx_call_count[-1]
lowest_count_idx = idx_call_count[0]
# get highest idx
flag_char = total_call_count[highest_count_idx][1][0]
flag += flag_char
print(n, total_call_count, highest_count_idx, flag)
300
We get 3 rsa public keys here, and there are no other attack method, just GCD them and found the GCD number to factor 3 n.
c1=18700320110367574655449823553009212724937318442101140581378358928204994827498139841897479168675123789374462637095265564472109735802305521045676412446455683615469865332270051569768255072111079626023422
e1=65537
n1=23795719145225386804055015945976331504878851440464956768596487167710701468817080174616923533397144140667518414516928416724767417895751634838329442802874972281385084714429143592029962130216053890866347
c2=27979368157170890767030069060194038526134599497456846620984054211906413024410400026053694007247773572972357106574636186987337336771777265971389911503143036021889778839064900818858188026318442675667707
e2=65537
n2=46914096084767238967814493997294740286838053572386502727910903794939283633197997427383196569296188299557978279732421725469482678512672280108542428152186999218210536447287087212703368704976239539968977
c3=24084879450015204136831744759734371350696278325227327049743434712309456808867398488915798176282769616955247276506807739249439515225213919008982824219656080794207250454008942016125074768497986930713993
e3=65537
n3=24543003393712692769038137223030855401835344295968717177380639898023646407807465197761211529143336105057325706788229129519925129413109571220297378014990693203802558792781281981621549760273376606206491
def int2text(message):
result=""
while message>0:
result = chr(int(message)%int(256))+ result
message=int(message)/int(256)
return result
import primefac
p1=primefac.gcd(n1,n2)
q1=n1/p1
d=primefac.modinv(e1,(p1-1)*(q1-1))%((p1-1)*(q1-1))
m1=pow(c1,d,n1)
print int2text(m1)
p2=p1
q2=n2/p2
d=primefac.modinv(e2,(p2-1)*(q2-1))%((p2-1)*(q2-1))
m2=pow(c2,d,n2)
print int2text(m2)
p3=primefac.gcd(n2,n3)
q3=n3/p3
d=primefac.modinv(e3,(p3-1)*(q3-1))%((p3-1)*(q3-1))
m3=pow(c3,d,n3)
print int2text(m3)
400
This challenge is a white-box protocol analysis aimed to break the authentication system.
Following is the work flow of this authenticatoin system:
- the user send a login request with username to the server
- the server send Nonce and ChallengeCookie = Base64Encode(RandomIV | AES128-CBC(RandomIV,Nonce | U | Timestamp, KS)) back to the user
- the user send the challenge response(R = SHA256(Nonce | P), where P is the password for authentication) to the server
the server verify whether the password and username is right or not. if right the server will issue a ticket to user, Ticket = Base64Encode(RandomIV | AES128-CBC(RandomIV,Identity | TicketTimestamp, KS)) where Identity = JSON string: { user: U, groups: [ G1, G2, ... ] }
where G1, G2, ... are the names of the groups that U belongs tothe user can use the ticket to run some command, if the username in the ticket is admin, we can run the command "getflag"
to break this authentication protocol, we can send a login request with username 'AAAAAAAA' + '{"user": "admin", "groups": ["admin"]}\x00' to the server.
the server will response with Base64Encode(RandomIV | AES128-CBC(RandomIV, Nonce | 'AAAAAAAA{"user": "admin", "groups": ["admin"]}\x00 | Timestamp, KS)).
since the AES128-CBC is a block cipher with CBC mode, we can use the AES128-CBC(RandomIV, Nonce | 'AAAAAAAA) as the newIV, and the remain part will be AES128-CBC(newIV,{"user": "admin", "groups": ["admin"]}\x00 | Timestamp), which is a valid admin ticket.
then we can use the ticket to run getflag command and get the flag.
from pwn import *
import base64
from Crypto.Cipher import AES
io=remote("localhost",9999)
def toNullTerminatedUtf8(s):
return unicode(s).encode("utf-8") + "\x00"
payload="\x01"+"A"*8+'{"user": "admin", "groups": ["admin"]}\x00'
io.send(payload)
data=io.recv(1000)
nounce=data[1:9]
cookie_b64=data[9:]
cookie = base64.b64decode(cookie_b64)
iv=cookie[:16]
fake_ticket=cookie[16:]
fake_ticket_b64=base64.b64encode(fake_ticket)
cmd="\x06"+fake_ticket_b64+"\x00"+"getflag\x00"
io.send(cmd)
io.interactive()
Reverse-Binary
100
We first find base64-encoded data from the pcap file.
Then, reverse the pyinstaller binary and modify the script to solve the challenge.
import struct, os, time, threading, urllib, requests, ctypes, base64
from Cryptodome.Cipher import AES, ARC4
from Cryptodome.Hash import SHA
infile = 'flag'
encfile = 'orig.CRYPTED'
keyfile = 'keyfile'
sz = 1024
bs = 16
def decrypt_request():
pcap_req = "35998fdb7fe3b7940b9375a68a654ff949c58dcb9b1aebb048d6aa74d905b7b0c6e04b404eb61129f92ad912703850201582ce39e77bfe739fec528741b202f8923a9f8d6303617d8e6e35a0d644115e238522c6d0cacd1afdae23050452c998e39a"
_hash_chksum = pcap_req[:40]
_hash_content = pcap_req[40:]
dec = ARC4.new(_hash_chksum.decode('hex'))
return dec.decrypt(_hash_content.decode('hex'))
# 'id=d1&key=2f87011fadc6c2f7376117867621b606&iv=95bc0ed56ab0e730b64cce91c9fe9390'
def generate_keyfile():
# n = hex(ord(id) + bs)
n = hex(ord('d1'.decode('hex')) + 16)
iv = "95bc0ed56ab0e730b64cce91c9fe9390".decode('hex')
key = "2f87011fadc6c2f7376117867621b606".decode('hex')
key = ''.join((chr(ord(x) ^ int(n, 16)) for x in key))
iv = ''.join((chr(ord(y) ^ int(n, 16)) for y in iv))
keyfile = open("keyfile", "wb")
keyfile.write(key + iv)
keyfile.close()
print(n, iv, key)
return True
def decrypt():
global keyfile
key = ''
iv = ''
if not os.path.exists(encfile):
exit(0)
while True:
time.sleep(10)
if os.path.exists(keyfile):
keyin = open(keyfile, 'rb')
key = keyin.read(bs)
iv = keyin.read(bs)
if len(key) != 0 and len(iv) != 0:
aes = AES.new(key, AES.MODE_CBC, iv)
fin = open(encfile, 'r')
fsz = struct.unpack('<H', fin.read(struct.calcsize('<H')))[0]
fout = open(infile, 'w')
fin.seek(2, 0)
while True:
data = fin.read(sz)
n = len(data)
if n == 0:
break
decrypted = aes.decrypt(data)
n = len(decrypted)
if fsz > n:
fout.write(decrypted)
else:
fout.write(decrypted[:fsz])
fsz -= n
fin.close()
os.remove(encfile)
break
print(decrypt_request())
generate_keyfile()
decrypt()
# ----Trend Microt CTF 2018. Flag for this challenge is: TMCTF{MJB1200}
200
A 32-bit shellcode injector using IAT hook. DragQueryFileW
in notepad.exe
is hooked. The shellcode is written into .text
section of shell32.dll
.
from ida_bytes import get_bytes, patch_bytes
buf = bytearray(get_bytes(0x4031A0, 376))
for i in xrange(len(buf)):
buf[i] ^= [0xDE, 0xAD, 0xF0, 0x0D][i % 4]
patch_bytes(0x4031A0, str(buf))
Run 32-bit notepad.exe and drop a file named "zdi_ftw", the rot13-enctypted flag is shown.
TMCTF{want_sum_iat_hooking}
300
The PE file is packed but it's easy to unpack. It detects debugger by IsDebuggerPresent
, and Virtual Machine by checking the presense of specific .sys file. Checks whether the hour field of current time is 5.
TMCTF{F14g1s::____1G}
400
A river crossing puzzle with a servant(7), a dog(4), a father(6), a mother(5), two sons(2,3) and two daughters(0,1).
sidev = 1
def side():
global sidev
t = sidev
sidev = 0 if sidev else 1
return t
def a(x, y = None):
if(y == None):
return "\xD0" + chr(side()) + chr(1 << x)
else:
return "\xD1" + chr(side()) + chr(1 << x) + chr(1 << y)
s = a(7, 4) + a(7)
s += a(7, 0) + a(7, 4)
s += a(5, 1) + a(5)
s += a(6, 5) + a(6)
s += a(7, 4) + a(5)
s += a(6, 5) + a(6)
s += a(6, 2) + a(7, 4)
s += a(7, 3) + a(7)
s += a(7, 4)
print(s.encode("hex").lower())
Reverse-Other
100
Run the binary directly, it ouput "flag is here", but run the binary under debugger, it output "nice try, punk".
so it seems like there are anti-dbg techniques deployed on this binary.
No worry, there are various ways to patch the binary and bypass the anti-debugging. once we find the function that output "flag is here", we found the flag. since the flag is reside in that function.
200
Simply unpack upx, some weird strings appear in main function.
I noticed this program will self-open and read all data in memory by using API monitor.
After some reversing , I found that it will compare the section name in memory.
Simply modify the code , let program compare the encrypted flag string with section name, you got flag in debugger instantly.
400
This Challenge is a python bytecode reversing challenge.
import sys
def verify_flag(flag): pass
verify_flag.__code__ = verify_flag.__code__.__class__( 1 , 20 , 9 , 67 , 'y\x0c\x00|\x00\x00d\x01\x00\x17\x01Wn%\x00\x01\x01\x01x9\x00|\x00\x00D]\x10\x00}\x01\x00|\x01\x00|\x01\x007}\x01\x00q\x19\x00W~\x01\x00n\x1b\x00Xx\x17\x00t\x00\x00rJ\x00|\x00\x00|\x00\x007}\x00\x00q7\x00W~\x00\x00y\x08\x00t\x01\x00\x01Wn\x07\x00\x01\x01\x01n\x01\x00Xt\x02\x00|\x00\x00\x83\x01\x00d\x01\x00k\x02\x00sx\x00t\x03\x00r|\x00t\x03\x00S|\x00\x00j\x04\x00d\x02\x00\x83\x01\x00s\x8f\x00t\x03\x00S|\x00\x00j\x05\x00d\x03\x00\x83\x01\x00s\xb4\x00t\x03\x00S|\x00\x00j\x06\x00d\x02\x00\x83\x01\x00}\x00\x00na\x00t\x02\x00|\x00\x00\x83\x01\x00}\x02\x00|\x00\x00j\x07\x00d\x02\x00d\x04\x00\x83\x02\x00d\x05\x00\x19j\x08\x00d\x03\x00d\x04\x00\x83\x02\x00d\x01\x00\x19}\x00\x00y \x00t\x02\x00|\x00\x00\x83\x01\x00d\x06\x00\x17|\x02\x00k\x02\x00s\x05\x01t\t\x00\x82\x01\x00Wn\x08\x00\x01\x01\x01t\x03\x00SXd&\x00\x01|\x00\x00d\x08\x00j\x06\x00d\t\x00d\n\x00\x83\x02\x00k\x02\x00r1\x01t\x03\x00St\n\x00t\x0b\x00|\x00\x00\x83\x02\x00}\x00\x00t\x02\x00|\x00\x00\x83\x01\x00}\x02\x00|\x02\x00d\x0b\x00k\x03\x00r\\\x01t\x03\x00St\x0c\x00|\x00\x00\x83\x01\x00}\x03\x00|\x03\x00|\x02\x00\x16d\x0c\x00k\x03\x00r|\x01t\x03\x00S|\x03\x00|\x02\x00\x15}\x04\x00t\r\x00|\x04\x00\x83\x01\x00d\r\x00k\x03\x00r\x9c\x01t\x03\x00Sg\x00\x00|\x00\x00D]\x10\x00}\x05\x00|\x05\x00|\x04\x00A^\x02\x00q\xa3\x01}\x00\x00t\x0e\x00t\x0f\x00|\x00\x00\x83\x01\x00\x83\x01\x00}\x06\x00d\x01\x00g\x01\x00d\x07\x00\x14}\x07\x00d\x01\x00g\x01\x00d\x07\x00\x14}\x08\x00d\x01\x00g\x01\x00d\x07\x00\x14}\t\x00d\x01\x00g\x01\x00d\x07\x00\x14}\n\x00d\x01\x00g\x01\x00d\x07\x00\x14}\x0b\x00d\x01\x00g\x01\x00d\x07\x00\x14}\x0c\x00d\x01\x00g\x01\x00d\x07\x00\x14}\r\x00d\x01\x00g\x01\x00d\x07\x00\x14}\x0e\x00d\x01\x00g\x01\x00d\x07\x00\x14}\x0f\x00d\x01\x00g\x01\x00d\x07\x00\x14}\x10\x00xS\x02t\x10\x00t\x02\x00|\x07\x00\x83\x01\x00\x83\x01\x00D]?\x02}\x11\x00x\xc6\x01t\x10\x00t\x02\x00|\x08\x00\x83\x01\x00d\x04\x00\x18\x83\x01\x00D]\xae\x01}\x12\x00|\x07\x00|\x11\x00c\x02\x00\x19|\x00\x00|\x11\x00|\x12\x00\x17\x19N\x03<|\x08\x00|\x11\x00\x19|\x00\x00|\x11\x00|\x12\x00\x17\x19\x17d\x0e\x00k\x04\x00r\xbb\x02t\x03\x00S|\x08\x00|\x11\x00c\x02\x00\x19|\x00\x00|\x11\x00|\x12\x00\x17\x197\x03<|\t\x00|\x11\x00c\x02\x00\x19|\x00\x00|\x11\x00|\x12\x00\x14\x19N\x03<|\n\x00|\x11\x00\x19|\x00\x00|\x11\x00|\x12\x00\x14\x19\x17d\x0e\x00k\x04\x00r\x0b\x03t\x03\x00S|\n\x00|\x11\x00c\x02\x00\x19|\x00\x00|\x11\x00|\x12\x00\x14\x197\x03<|\x0b\x00|\x11\x00c\x02\x00\x19|\x00\x00d\x0f\x00|\x11\x00|\x12\x00\x14\x17\x19N\x03<|\x0c\x00|\x11\x00\x19|\x00\x00d\x0f\x00|\x11\x00|\x12\x00\x14\x17\x19\x17d\x0e\x00k\x04\x00rc\x03t\x03\x00S|\x0c\x00|\x11\x00c\x02\x00\x19|\x00\x00d\x0f\x00|\x11\x00|\x12\x00\x14\x17\x197\x03<|\r\x00|\x11\x00c\x02\x00\x19|\x06\x00d\x0f\x00|\x11\x00|\x12\x00\x14\x17\x19N\x03<|\x0e\x00|\x11\x00\x19|\x06\x00d\x0f\x00|\x11\x00|\x12\x00\x14\x17\x19\x17d\x0e\x00k\x04\x00r\xbf\x03t\x03\x00S|\x0e\x00|\x11\x00c\x02\x00\x19|\x06\x00d\x0f\x00|\x11\x00|\x12\x00\x14\x17\x197\x03<|\x0f\x00|\x11\x00c\x02\x00\x19|\x06\x00|\x11\x00|\x12\x00\x17\x19N\x03<|\x10\x00|\x11\x00\x19|\x06\x00|\x11\x00|\x12\x00\x17\x19\x17d\x0e\x00k\x04\x00r\x13\x04t\x03\x00S|\x10\x00|\x11\x00c\x02\x00\x19|\x06\x00|\x11\x00|\x12\x00\x17\x197\x03<q}\x02W|\x07\x00|\x11\x00c\x02\x00\x19d\x10\x007\x03<|\t\x00|\x11\x00c\x02\x00\x19d\x10\x007\x03<|\x0b\x00|\x11\x00c\x02\x00\x19d\x10\x007\x03<|\r\x00|\x11\x00c\x02\x00\x19d\x10\x007\x03<|\x0f\x00|\x11\x00c\x02\x00\x19d\x10\x007\x03<|\x0c\x00|\x11\x00c\x02\x00\x19d\x0f\x007\x03<|\x10\x00|\x11\x00c\x02\x00\x19d\x04\x007\x03<q`\x02WxJ\x00|\x07\x00|\t\x00|\x0b\x00|\r\x00|\x0f\x00|\n\x00|\x0c\x00|\x0e\x00|\x10\x00g\t\x00D]\'\x00}\x13\x00x\x1e\x00|\x13\x00D]\x16\x00}\x05\x00|\x05\x00d\x0e\x00k\x04\x00r\xd2\x04t\x03\x00Sq\xd2\x04Wq\xc5\x04Wd\x11\x00j\x11\x00t\n\x00t\r\x00|\x07\x00\x83\x02\x00\x83\x01\x00d\x12\x00k\x03\x00r\x12\x05t\x03\x00Sy&\x00d\x11\x00j\x11\x00t\n\x00t\r\x00|\x08\x00\x83\x02\x00\x83\x01\x00d\x13\x00k\x03\x00r7\x05t\x03\x00SWn\x12\x00\x04t\x12\x00k\n\x00rL\x05\x01\x01\x01t\x03\x00SXd\x11\x00j\x11\x00t\n\x00t\r\x00|\t\x00\x83\x02\x00\x83\x01\x00d\x14\x00k\x03\x00ro\x05t\x03\x00St\x13\x00|\n\x00\x83\x01\x00d\'\x00k\x03\x00r\x85\x05t\x03\x00Sd\x11\x00j\x11\x00t\n\x00t\r\x00|\x0b\x00\x83\x02\x00\x83\x01\x00d\x1a\x00k\x03\x00r\xac\x05d\x1b\x00GHt\x03\x00Sd\x11\x00j\x11\x00t\n\x00t\r\x00|\x0c\x00\x83\x02\x00\x83\x01\x00d\x1c\x00k\x03\x00r\xd3\x05d\x1d\x00GHt\x03\x00Sd\x11\x00j\x11\x00t\n\x00t\r\x00|\r\x00\x83\x02\x00\x83\x01\x00d\x1e\x00k\x03\x00r\xfa\x05d\x1f\x00GHt\x03\x00Sd\x11\x00j\x11\x00t\n\x00t\r\x00|\x0e\x00\x83\x02\x00\x83\x01\x00d \x00k\x03\x00r!\x06d!\x00GHt\x03\x00Sd\x11\x00j\x11\x00t\n\x00t\r\x00|\x0f\x00\x83\x02\x00\x83\x01\x00d"\x00k\x03\x00rH\x06d#\x00GHt\x03\x00Sd\x11\x00j\x11\x00t\n\x00t\r\x00|\x10\x00\x83\x02\x00\x83\x01\x00d$\x00k\x03\x00ro\x06d%\x00GHt\x03\x00St\x00\x00S' , (None, 0, 'TMCTF{', '}', 1, -1, 7, 5, 'ReadEaring', 'adEa', 'dHer', 24, 9, 'h', 255, 8, 32, '', 'R) +6', 'l1:C(', ' RP%A', 236, 108, 102, 169, 93, ' L30Z', 'X2', ' j36~', 's2', ' M2S+', 'X3', '4e\x9c{E', 'S3', '6!2$D', 'X4', ']PaSs', 'S4', 10, (236, 108, 102, 169, 93)) , ('True', '\xe0\xa1\xb5\xe0\xa1\xb5HA', 'len', 'False', 'startswith', 'endswith', 'replace', 'split', 'rsplit', 'AssertionError', 'map', 'ord', 'sum', 'chr', 'list', 'reversed', 'xrange', 'join', 'ValueError', 'tuple') , ('inval', 'c', 'l', 's', 'sdl', 'x', 'ROFL', 'KYRYK', 'QQRTQ', 'KYRYJ', 'QQRTW', 'KYRYH', 'QQRTE', 'KYRYG', 'QQRTR', 'KYRYF', 'QQRTY', 'i', 'j', 'ary') , 'flag.py' , 'verify_flag' , 1337 , '\x00\x01\x03\x01\x0c\x01\x03\x01\r\x01\x0e\x02\x07\x02\t\x01\x0e\x02\x03\x02\x03\x01\x08\x01\x03\x01\x04\x02\x12\x01\x06\x01\x04\x01\x0f\x01\x04\x02\x0f\x01\x04\x01\x12\x03\x0c\x01&\x01\x03\x01 \x01\x03\x01\x05\x02\x04\x02\x18\x01\x04\x01\x0f\x01\x0c\x01\x0c\x01\x04\x01\x0c\x01\x10\x01\x04\x01\n\x01\x12\x01\x04\x01\x1d\x01\x12\x01\r\x01\r\x01\r\x01\r\x01\r\x01\r\x01\r\x01\r\x01\r\x01\r\x01\x19\x01\x1d\x01\x18\x01\x1c\x01\x04\x01\x18\x01\x18\x01\x1c\x01\x04\x01\x18\x01\x1c\x01 \x01\x04\x01\x1c\x01\x1c\x01 \x01\x04\x01\x1c\x01\x18\x01\x1c\x01\x04\x01\x1c\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x14\x01(\x01\r\x01\x0c\x01\x0c\x01\x1e\x01\x04\x01\x03\x01\x1e\x01\x08\x01\r\x01\x05\x07\x1e\x01\x04\x01\x12\x01\x04\x01\x1e\x01\x05\x01\x04\x01\x1e\x01\x05\x01\x04\x01\x1e\x01\x05\x01\x04\x01\x1e\x01\x05\x01\x04\x01\x1e\x01\x05\x01\x04\x01\x1e\x01\x05\x01\x04\x01' )
if __name__ == "__main__":
if len(sys.argv) != 2:
print "Usage:"
print " %s flag" % sys.argv[0]
else:
if verify_flag(sys.argv[1]):
print "%s is the correct flag!" % sys.argv[1]
else:
print "Better luck next time"
Seems like we have to reverse the python bytecode verify_flag.code.
We try to decompile the bytecode by crafting a pyc file and use uncompyle6 to decompile, but it doesn't work since the code contains non-ascii characters.
Finally, We sucessfully decompiled the bytecode by craft a Code2 object and call uncompyle6.main.decompile() directly
import xdis
import sys
from xdis.code import Code2
from xdis.bytecode import get_instructions_bytes
import uncompyle6
argcount = 1
nlocals = 20
stacksize = 9
flags = 67
code = b'y\x0c\x00|\x00\x00d\x01\x00\x17\x01Wn%\x00\x01\x01\x01x9\x00|\x00\x00D]\x10\x00}\x01\x00|\x01\x00|\x01\x007}\x01\x00q\x19\x00W~\x01\x00n\x1b\x00Xx\x17\x00t\x00\x00rJ\x00|\x00\x00|\x00\x007}\x00\x00q7\x00W~\x00\x00y\x08\x00t\x01\x00\x01Wn\x07\x00\x01\x01\x01n\x01\x00Xt\x02\x00|\x00\x00\x83\x01\x00d\x01\x00k\x02\x00sx\x00t\x03\x00r|\x00t\x03\x00S|\x00\x00j\x04\x00d\x02\x00\x83\x01\x00s\x8f\x00t\x03\x00S|\x00\x00j\x05\x00d\x03\x00\x83\x01\x00s\xb4\x00t\x03\x00S|\x00\x00j\x06\x00d\x02\x00\x83\x01\x00}\x00\x00na\x00t\x02\x00|\x00\x00\x83\x01\x00}\x02\x00|\x00\x00j\x07\x00d\x02\x00d\x04\x00\x83\x02\x00d\x05\x00\x19j\x08\x00d\x03\x00d\x04\x00\x83\x02\x00d\x01\x00\x19}\x00\x00y \x00t\x02\x00|\x00\x00\x83\x01\x00d\x06\x00\x17|\x02\x00k\x02\x00s\x05\x01t\t\x00\x82\x01\x00Wn\x08\x00\x01\x01\x01t\x03\x00SXd&\x00\x01|\x00\x00d\x08\x00j\x06\x00d\t\x00d\n\x00\x83\x02\x00k\x02\x00r1\x01t\x03\x00St\n\x00t\x0b\x00|\x00\x00\x83\x02\x00}\x00\x00t\x02\x00|\x00\x00\x83\x01\x00}\x02\x00|\x02\x00d\x0b\x00k\x03\x00r\\\x01t\x03\x00St\x0c\x00|\x00\x00\x83\x01\x00}\x03\x00|\x03\x00|\x02\x00\x16d\x0c\x00k\x03\x00r|\x01t\x03\x00S|\x03\x00|\x02\x00\x15}\x04\x00t\r\x00|\x04\x00\x83\x01\x00d\r\x00k\x03\x00r\x9c\x01t\x03\x00Sg\x00\x00|\x00\x00D]\x10\x00}\x05\x00|\x05\x00|\x04\x00A^\x02\x00q\xa3\x01}\x00\x00t\x0e\x00t\x0f\x00|\x00\x00\x83\x01\x00\x83\x01\x00}\x06\x00d\x01\x00g\x01\x00d\x07\x00\x14}\x07\x00d\x01\x00g\x01\x00d\x07\x00\x14}\x08\x00d\x01\x00g\x01\x00d\x07\x00\x14}\t\x00d\x01\x00g\x01\x00d\x07\x00\x14}\n\x00d\x01\x00g\x01\x00d\x07\x00\x14}\x0b\x00d\x01\x00g\x01\x00d\x07\x00\x14}\x0c\x00d\x01\x00g\x01\x00d\x07\x00\x14}\r\x00d\x01\x00g\x01\x00d\x07\x00\x14}\x0e\x00d\x01\x00g\x01\x00d\x07\x00\x14}\x0f\x00d\x01\x00g\x01\x00d\x07\x00\x14}\x10\x00xS\x02t\x10\x00t\x02\x00|\x07\x00\x83\x01\x00\x83\x01\x00D]?\x02}\x11\x00x\xc6\x01t\x10\x00t\x02\x00|\x08\x00\x83\x01\x00d\x04\x00\x18\x83\x01\x00D]\xae\x01}\x12\x00|\x07\x00|\x11\x00c\x02\x00\x19|\x00\x00|\x11\x00|\x12\x00\x17\x19N\x03<|\x08\x00|\x11\x00\x19|\x00\x00|\x11\x00|\x12\x00\x17\x19\x17d\x0e\x00k\x04\x00r\xbb\x02t\x03\x00S|\x08\x00|\x11\x00c\x02\x00\x19|\x00\x00|\x11\x00|\x12\x00\x17\x197\x03<|\t\x00|\x11\x00c\x02\x00\x19|\x00\x00|\x11\x00|\x12\x00\x14\x19N\x03<|\n\x00|\x11\x00\x19|\x00\x00|\x11\x00|\x12\x00\x14\x19\x17d\x0e\x00k\x04\x00r\x0b\x03t\x03\x00S|\n\x00|\x11\x00c\x02\x00\x19|\x00\x00|\x11\x00|\x12\x00\x14\x197\x03<|\x0b\x00|\x11\x00c\x02\x00\x19|\x00\x00d\x0f\x00|\x11\x00|\x12\x00\x14\x17\x19N\x03<|\x0c\x00|\x11\x00\x19|\x00\x00d\x0f\x00|\x11\x00|\x12\x00\x14\x17\x19\x17d\x0e\x00k\x04\x00rc\x03t\x03\x00S|\x0c\x00|\x11\x00c\x02\x00\x19|\x00\x00d\x0f\x00|\x11\x00|\x12\x00\x14\x17\x197\x03<|\r\x00|\x11\x00c\x02\x00\x19|\x06\x00d\x0f\x00|\x11\x00|\x12\x00\x14\x17\x19N\x03<|\x0e\x00|\x11\x00\x19|\x06\x00d\x0f\x00|\x11\x00|\x12\x00\x14\x17\x19\x17d\x0e\x00k\x04\x00r\xbf\x03t\x03\x00S|\x0e\x00|\x11\x00c\x02\x00\x19|\x06\x00d\x0f\x00|\x11\x00|\x12\x00\x14\x17\x197\x03<|\x0f\x00|\x11\x00c\x02\x00\x19|\x06\x00|\x11\x00|\x12\x00\x17\x19N\x03<|\x10\x00|\x11\x00\x19|\x06\x00|\x11\x00|\x12\x00\x17\x19\x17d\x0e\x00k\x04\x00r\x13\x04t\x03\x00S|\x10\x00|\x11\x00c\x02\x00\x19|\x06\x00|\x11\x00|\x12\x00\x17\x197\x03<q}\x02W|\x07\x00|\x11\x00c\x02\x00\x19d\x10\x007\x03<|\t\x00|\x11\x00c\x02\x00\x19d\x10\x007\x03<|\x0b\x00|\x11\x00c\x02\x00\x19d\x10\x007\x03<|\r\x00|\x11\x00c\x02\x00\x19d\x10\x007\x03<|\x0f\x00|\x11\x00c\x02\x00\x19d\x10\x007\x03<|\x0c\x00|\x11\x00c\x02\x00\x19d\x0f\x007\x03<|\x10\x00|\x11\x00c\x02\x00\x19d\x04\x007\x03<q`\x02WxJ\x00|\x07\x00|\t\x00|\x0b\x00|\r\x00|\x0f\x00|\n\x00|\x0c\x00|\x0e\x00|\x10\x00g\t\x00D]\'\x00}\x13\x00x\x1e\x00|\x13\x00D]\x16\x00}\x05\x00|\x05\x00d\x0e\x00k\x04\x00r\xd2\x04t\x03\x00Sq\xd2\x04Wq\xc5\x04Wd\x11\x00j\x11\x00t\n\x00t\r\x00|\x07\x00\x83\x02\x00\x83\x01\x00d\x12\x00k\x03\x00r\x12\x05t\x03\x00Sy&\x00d\x11\x00j\x11\x00t\n\x00t\r\x00|\x08\x00\x83\x02\x00\x83\x01\x00d\x13\x00k\x03\x00r7\x05t\x03\x00SWn\x12\x00\x04t\x12\x00k\n\x00rL\x05\x01\x01\x01t\x03\x00SXd\x11\x00j\x11\x00t\n\x00t\r\x00|\t\x00\x83\x02\x00\x83\x01\x00d\x14\x00k\x03\x00ro\x05t\x03\x00St\x13\x00|\n\x00\x83\x01\x00d\'\x00k\x03\x00r\x85\x05t\x03\x00Sd\x11\x00j\x11\x00t\n\x00t\r\x00|\x0b\x00\x83\x02\x00\x83\x01\x00d\x1a\x00k\x03\x00r\xac\x05d\x1b\x00GHt\x03\x00Sd\x11\x00j\x11\x00t\n\x00t\r\x00|\x0c\x00\x83\x02\x00\x83\x01\x00d\x1c\x00k\x03\x00r\xd3\x05d\x1d\x00GHt\x03\x00Sd\x11\x00j\x11\x00t\n\x00t\r\x00|\r\x00\x83\x02\x00\x83\x01\x00d\x1e\x00k\x03\x00r\xfa\x05d\x1f\x00GHt\x03\x00Sd\x11\x00j\x11\x00t\n\x00t\r\x00|\x0e\x00\x83\x02\x00\x83\x01\x00d \x00k\x03\x00r!\x06d!\x00GHt\x03\x00Sd\x11\x00j\x11\x00t\n\x00t\r\x00|\x0f\x00\x83\x02\x00\x83\x01\x00d"\x00k\x03\x00rH\x06d#\x00GHt\x03\x00Sd\x11\x00j\x11\x00t\n\x00t\r\x00|\x10\x00\x83\x02\x00\x83\x01\x00d$\x00k\x03\x00ro\x06d%\x00GHt\x03\x00St\x00\x00S'
consts = (None, 0, 'TMCTF{', '}', 1, -1, 7, 5, 'ReadEaring', 'adEa', 'dHer', 24, 9, 'h', 255, 8, 32, '', 'R) +6', 'l1:C(', ' RP%A', 236, 108, 102, 169, 93, ' L30Z', 'X2', ' j36~', 's2', ' M2S+', 'X3', '4e\x9c{E', 'S3', '6!2$D', 'X4', ']PaSs', 'S4', 10, (236, 108, 102, 169, 93))
names = ('True', '\xe0\xa1\xb5\xe0\xa1\xb5HA', 'len', 'False', 'startswith', 'endswith', 'replace', 'split', 'rsplit', 'AssertionError', 'map', 'ord', 'sum', 'chr', 'list', 'reversed', 'xrange', 'join', 'ValueError', 'tuple')
varnames = ('inval', 'c', 'l', 's', 'sdl', 'x', 'ROFL', 'KYRYK', 'QQRTQ', 'KYRYJ', 'QQRTW', 'KYRYH', 'QQRTE', 'KYRYG', 'QQRTR', 'KYRYF', 'QQRTY', 'i', 'j', 'ary')
filename = 'flag.py'
name = 'verify_flag'
firstlineno = 1337
lnotab = '\x00\x01\x03\x01\x0c\x01\x03\x01\r\x01\x0e\x02\x07\x02\t\x01\x0e\x02\x03\x02\x03\x01\x08\x01\x03\x01\x04\x02\x12\x01\x06\x01\x04\x01\x0f\x01\x04\x02\x0f\x01\x04\x01\x12\x03\x0c\x01&\x01\x03\x01 \x01\x03\x01\x05\x02\x04\x02\x18\x01\x04\x01\x0f\x01\x0c\x01\x0c\x01\x04\x01\x0c\x01\x10\x01\x04\x01\n\x01\x12\x01\x04\x01\x1d\x01\x12\x01\r\x01\r\x01\r\x01\r\x01\r\x01\r\x01\r\x01\r\x01\r\x01\r\x01\x19\x01\x1d\x01\x18\x01\x1c\x01\x04\x01\x18\x01\x18\x01\x1c\x01\x04\x01\x18\x01\x1c\x01 \x01\x04\x01\x1c\x01\x1c\x01 \x01\x04\x01\x1c\x01\x18\x01\x1c\x01\x04\x01\x1c\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x14\x01(\x01\r\x01\x0c\x01\x0c\x01\x1e\x01\x04\x01\x03\x01\x1e\x01\x08\x01\r\x01\x05\x07\x1e\x01\x04\x01\x12\x01\x04\x01\x1e\x01\x05\x01\x04\x01\x1e\x01\x05\x01\x04\x01\x1e\x01\x05\x01\x04\x01\x1e\x01\x05\x01\x04\x01\x1e\x01\x05\x01\x04\x01\x1e\x01\x05\x01\x04\x01'
co = Code2(argcount, 0, nlocals, stacksize, flags, code, consts, names, varnames, filename, name, firstlineno, lnotab, (), ())
version = 2.7
timestamp = 1536287532
code_objects = {co: co}
source_size = None
is_pypy = False
magic_int = 62211
uncompyle6.main.decompile(version, co, sys.stdout, None, False, timestamp, False, code_objects=code_objects, source_size=source_size, is_pypy=is_pypy, magic_int=magic_int, mapstream=None, do_fragments=None)
following is the decompiled code
# uncompyle6 version 3.2.3
# Python bytecode 2.7 (62211)
# Decompiled from: Python 3.7.0 (default, Jul 15 2018, 10:44:58)
# [GCC 8.1.1 20180531]
# Embedded file name: flag.py
# Compiled at: 2018-09-07 10:32:12
try:
inval + 0
except:
for c in inval:
c += c
else:
del c
else:
while 1:
if True:
inval += inval
else:
del inval
try:
ࡵࡵHA
except:
pass
if len(inval) == 0 or False:
return False
if not inval.startswith('TMCTF{'):
return False
if not inval.endswith('}'):
return False
inval = inval.replace('TMCTF{')
else:
l = len(inval)
inval = inval.split('TMCTF{', 1)[-1].rsplit('}', 1)[0]
try:
assert len(inval) + 7 == l
except:
return False
10
if inval == ('ReadEaring').replace('adEa', 'dHer'):
return False
inval = map(ord, inval)
l = len(inval)
if l != 24:
return False
s = sum(inval)
if s % l != 9:
return False
sdl = s / l
if chr(sdl) != 'h':
return False
inval = [ x ^ sdl for x in inval ]
ROFL = list(reversed(inval))
KYRYK = [0] * 5
QQRTQ = [0] * 5
KYRYJ = [0] * 5
QQRTW = [0] * 5
KYRYH = [0] * 5
QQRTE = [0] * 5
KYRYG = [0] * 5
QQRTR = [0] * 5
KYRYF = [0] * 5
QQRTY = [0] * 5
for i in xrange(len(KYRYK)):
for j in xrange(len(QQRTQ) - 1):
KYRYK[i] ^= inval[i + j]
if QQRTQ[i] + inval[i + j] > 255:
return False
QQRTQ[i] += inval[i + j]
KYRYJ[i] ^= inval[i * j]
if QQRTW[i] + inval[i * j] > 255:
return False
QQRTW[i] += inval[i * j]
KYRYH[i] ^= inval[8 + i * j]
if QQRTE[i] + inval[8 + i * j] > 255:
return False
QQRTE[i] += inval[8 + i * j]
KYRYG[i] ^= ROFL[8 + i * j]
if QQRTR[i] + ROFL[8 + i * j] > 255:
return False
QQRTR[i] += ROFL[8 + i * j]
KYRYF[i] ^= ROFL[i + j]
if QQRTY[i] + ROFL[i + j] > 255:
return False
QQRTY[i] += ROFL[i + j]
KYRYK[i] += 32
KYRYJ[i] += 32
KYRYH[i] += 32
KYRYG[i] += 32
KYRYF[i] += 32
QQRTE[i] += 8
QQRTY[i] += 1
for ary in [KYRYK, KYRYJ, KYRYH, KYRYG, KYRYF, QQRTW, QQRTE, QQRTR, QQRTY]:
for x in ary:
if x > 255:
return False
if ('').join(map(chr, KYRYK)) != 'R) +6':
return False
try:
if ('').join(map(chr, QQRTQ)) != 'l1:C(':
return False
except ValueError:
return False
if ('').join(map(chr, KYRYJ)) != ' RP%A':
return False
if tuple(QQRTW) != (236, 108, 102, 169, 93):
return False
if ('').join(map(chr, KYRYH)) != ' L30Z':
print 'X2'
return False
if ('').join(map(chr, QQRTE)) != ' j36~':
print 's2'
return False
if ('').join(map(chr, KYRYG)) != ' M2S+':
print 'X3'
return False
if ('').join(map(chr, QQRTR)) != '4e\x9c{E':
print 'S3'
return False
if ('').join(map(chr, KYRYF)) != '6!2$D':
print 'X4'
return False
if ('').join(map(chr, QQRTY)) != ']PaSs':
print 'S4'
return False
return True
By reversing the decompiled code, we realized that it is very easy to get the flag by z3 solver.
from z3 import *
flag = []
constraints = []
sum_flag=2505
for i in range(24):
flag.append(BitVec('x%d' % i, 16))
constraints.append(flag[i]<0x7f)
constraints.append(flag[i]>0x20)
sum_flag-=flag[i]
constraints.append(sum_flag==0)
flag_enc = [x ^ 104 for x in flag]
flag_enc_rev = list(reversed(flag_enc))
aa = [0] * 5
bb = [0] * 5
cc = [0] * 5
dd = [0] * 5
ee = [0] * 5
ff = [0] * 5
gg = [0] * 5
hh = [0] * 5
ii = [0] * 5
jj = [0] * 5
for i in range(len(aa)):
for j in range(len(bb) - 1):
aa[i] ^= flag_enc[i + j]
#if bb[i] + flag_enc[i + j] > 255:
# return False
bb[i] += flag_enc[i + j]
cc[i] ^= flag_enc[i * j]
#if dd[i] + flag_enc[i * j] > 255:
# return False
dd[i] += flag_enc[i * j]
ee[i] ^= flag_enc[8 + i * j]
#if ff[i] + flag_enc[8 + i * j] > 255:
# return False
ff[i] += flag_enc[8 + i * j]
gg[i] ^= flag_enc_rev[8 + i * j]
#if hh[i] + flag_enc_rev[8 + i * j] > 255:
# return False
hh[i] += flag_enc_rev[8 + i * j]
ii[i] ^= flag_enc_rev[i + j]
#if jj[i] + flag_enc_rev[i + j] > 255:
# return False
jj[i] += flag_enc_rev[i + j]
aa[i] += 32
cc[i] += 32
ee[i] += 32
gg[i] += 32
ii[i] += 32
ff[i] += 8
jj[i] += 1
#for ary in [aa, cc, ee, gg, ii, dd, ff, hh, jj]:
# for x in ary:
# if x > 255:
# return False
compare = list(map(ord, 'R) +6'))
for i in range(5):
constraints.append(aa[i] == compare[i])
compare = list(map(ord, 'l1:C('))
for i in range(5):
constraints.append(bb[i] == compare[i])
compare = list(map(ord, ' RP%A'))
for i in range(5):
constraints.append(cc[i] == compare[i])
compare = (236, 108, 102, 169, 93)
for i in range(5):
constraints.append(dd[i] == compare[i])
compare = list(map(ord, ' L30Z'))
for i in range(5):
constraints.append(ee[i] == compare[i])
compare = list(map(ord, ' j36~'))
for i in range(5):
constraints.append(ff[i] == compare[i])
compare = list(map(ord, ' M2S+'))
for i in range(5):
constraints.append(gg[i] == compare[i])
compare = list(map(ord, '4e\x9c{E'))
for i in range(5):
constraints.append(hh[i] == compare[i])
compare = list(map(ord, '6!2$D'))
for i in range(5):
constraints.append(ii[i] == compare[i])
compare = list(map(ord, ']PaSs'))
for i in range(5):
constraints.append(jj[i] == compare[i])
#print(constraints)
print(solve(constraints))
Forensics-crypto1
100
25 x 25 qr-code version 2
type infomation bits: 010101111x101101, ECC level: Q, mask: 7
( ((row + column) mod 2) + ((row * column) mod 3) ) mod 2 == 0
Error corrections available but data is removed (right side)
This is readed as follows
1001110110001001110110001001010001101110010001101110010001101011110101000010111101011000100111010011001100101101010011100011000101110110011010100011010001111101000011101100110110101001110100101101011111110100010101100001100011100101110010101000111111011001110011111111011011001010101110110100101110100101100110111100001001100001100100100001111010000000000
QR CODE has many modes, we try to use byte mode to deocode it.(4 bit mode, 8 bit length, the size of per data block is 8)
By trying to decode the QR CODE manually, we can find that the flag is ended with <font color="red">N1nj4}</font>. So the length of this string is 0b00010100.
The Data and ECC block can be read under the rule as follows. <font color="blue">But we need to read it from offset 4 + 8</font>.
Due to the fact that the ecc level of this QR CODE is %25, we just need to patch some known letters as “TWCTF”,etc.
But we need to know how to patch it.
XORed
we need to patch the header of the QR CODE, mode and lemgth and then we need to XOR them with mask.
After patching known bytes, we can scan the QR CODE.
flag is here
<font color="red">TMCTF{QRc0d3-N1nj4}</font>
200
Decompiling pyinstaller shows the sourcecode.
$ cat OceanOfSockets.py
...
def request():
try:
connection = httplib.HTTPConnection(sys.argv[1], sys.argv[2])
connection.request('GET', '/tmctf.html')
resTMCF = connection.getresponse()
readData = resTMCF.read()
if 'OceanOfSockets' in readData:
headers = {'User-Agent': 'Mozilla Firefox, Edge/12',
'Content-type': 'text/html',
'Cookie': '%|r%uL5bbA0F?5bC0E9b0_4b2?N'}
connection.request('GET', '/index.html', '', headers)
else:
sys.exit(0)
except:
pass
...
There doesn't seem to be much information except the suspicious cookie.
While thinking about the flag format (which is TMCTF{}
), I realized it should be a simple addition algorithm used on Cookie.
>>> [chr((ord(i) + 47)) for i in '%|r%uL5bbA0F?5bC0E9b0_4b2?N']
['T', '\xab', '\xa1', 'T', '\xa4', '{', 'd', '\x91', '\x91', 'p', '_', 'u', 'n', 'd', '\x91', 'r', '_', 't', 'h', '\x91', '_', '\x8e', 'c', '\x91', 'a', 'n', '}']
Now it sounds like some of characters are not displayed properly. I decided to mod a byte to leak remaining ambiguous bytes.
>>> [chr((ord(i) + 47) % 0x5e) for i in '%|r%uL5bbA0F?5bC0E9b0_4b2?N']
['T', 'M', 'C', 'T', 'F', '\x1d', '\x06', '3', '3', '\x12', '\x01', '\x17', '\x10', '\x06', '3', '\x14', '\x01', '\x16', '\n', '3', '\x01', '0', '\x05', '3', '\x03', '\x10', '\x1f']
Merging above results will print the flag
flag: TMCTF{d33p_und3r_th3_0c3an}
400
We got these informations from challenge:
n = 144 and l = 288
fi (x) = x XOR ki
unknown h
We known a couple of plaintext/cipher, so it's known plaintext attack.
Here the F function of feistel is XOR, all differences are transmitted by probability 1. So we can decrypt all cipher over these conditions.
Fistly, we do some pre-works:
we don't know h, so we need to try alot of time, and finally I found:
true_l=xor(lm,xor(rc,r))
We guess the h=5 or like 5 round's result, so we can solve it:
m1="010000010110111000100000011000010111000001110000011011000110010100100000011000010110111001100100001000000110000101101110001000000110111101110010011000010110111001100111011001010010000001110111011001010110111001110100001000000111010001101111001000000101010001110010011001010110111001100100"
c1="000100100011000101110101001101100110001100110001001110100011110101100000011110010010111000110011001110000000110100100101011111000011000000100001010000100110011100100001011000000111001101110100011011100110000000100000011011010110001001100100001011010110111001100110001010110110110101110001"
c="000000110000111001011100001000000001100100101100000100100111111000001001000001100000001100001001000100100010011101001010011000010111100100100010010101110100010001000010010101010100010101111111010001000110000001101001011111110111100001100101011000010010001001001011011000100111001001101011"
def xor(bin1,bin2):
assert len(bin1)==len(bin2)
s=""
for i in range(len(bin1)):
s+=str(int(bin1[i])^int(bin2[i]))
return s
from Crypto.Util.number import long_to_bytes
def show(x):
print long_to_bytes(int(x,2))
lm=m1[0:144]
rm=m1[144:]
lc=c1[0:144]
rc=c1[144:]
l=c[0:144]
r=c[144:]
true_l=xor(lm,xor(rc,r))
true_r=xor(xor(xor(xor(lc,l),true_l),lm),rm)
show(true_l+true_r)
Forensics-crypto2
200
Dump HTTP upload requests and you will get mausoleum.exe.
From there, decompile the exe file (omg so many pyinstaller binary)
I was somehow unable to decrypt the python file (I changed some of bits in headers, still it didn't work)
so I decided to remove useless letters from the notepad and get the flag. Guess what? I successfully submitted it on the first guess.
TMCTF{the_s3cr3t_i$_unE@rth3d}
Misc
100
Foremost extract a zip file and unzip it to get flag:
200
The challenges provides a pcap file and a python script. The python script reads a txt file into an array and uses it as training data for DBSCAN. So I guess the purpose of this challenge is to extract the data from the pcap.
I use strings
to observe that the data of icmp packet looks like the data we want ,so I extract them with the following command
tshark -r ./traffic.pcap -Y "icmp and ip.src_host==192.168.0.17" -T fields -e data
Decode them and apply them to the python script. But the model outputs nothing. So I decide to plot it directly.
And the flag is FLAG:1