ふるつき

v(*'='*)v かに

Securinets Prequals 2K19 writeup

We made a new team zer0pts and attended Securinets Prequals 2K19. We got 23393 points and reached 2nd place!! Thanks to all the admins for this great CTF! I solved some of the challenges and I'm going to write up them.

f:id:Furutsuki:20190325085517p:plain

[Misc 200pts (321 solves)]HIDDEN

description:

My friend asked me to find his hidden flag in this link .. Can you help me? Link

Author:Tr'GFx

The web page of the link just has a simple sentence: "Flag is somewhere here". When I jumped to the link for the first time, Firefox alerted against the self-signed certificate. So I checked the certificate and found the flag at "Verified by" column. Securinets{HiDDeN_D@tA_In_S3lF_S3iGnEd_CeRtifICates}

[Misc 331pts (184 solves)]EZ

description:

Welcome to Securinets, this one is an easy one.

Please make sure you hash the WORD with sha1 (lowercase hash letter)

The final flag is : Securinets{the_hash_of_the_word}

Pic Link

Author:BlueWhale

We were given pic.zip which has an image file pic.png in it. By using stegsolve, I noticed that something is hidden in the Least Significant Bit. I wrote the following script to extract hidden data.

from PIL import Image

img = Image.open("pic.png")
w, h = img.size

s = ''
c = ''
for y in range(3):
    for x in range(w):
        r, g, b = img.getpixel((x, y))
        c += str(r&1) + str(g&1) + str(b&1)
        if len(c) >= 8:
            s += chr(int(c[:8], 2))
            c = c[8:]
print(s.rstrip('\x00'))

Then I got phrases from Sherlock Holmes. <DETELED_WORD> (!) is "memorandum-book", so the flag is Securinets{b47f0d2a8866a75696f94a515d0cdf54c7ea3174}.

--START--
"The fact is that upon his entrance I had instantly recognized the extreme
personal danger in which I lay. The only conceivable escape for him lay in silencing
my tongue. In an instant I had slipped the revolver from the drawer into my
pocket and was covering him through the cloth. At his remark I drew the weapon
out and laid it cocked upon the table. He still smiled and blinked, but there was
something about his eyes which made me feel very glad that I had it there,
"You evidently don't know me,' said he.
"'On the contrary,' I answered, 'I think it is fairly evident that I do. Pray take
a chair. I can spare you five minutes if you have anything to say.'
"'All that I have to say has already crossed your mind,' said he.
"'Then possibly my answer has crossed yours,' I replied.
"'You stand fast?'
"'Absolutely.'
"He clapped his hand into his pocket, and I raised the pistol from the table.
But he merely drew out a <DETELED_WORD> in which he had scribbled some
dates.
"You crossed my path on the fourth of January,' said he. 'On the twenty-third
you incommoded me; by the middle of February I was seriously inconvenienced
by you; at the end of March I was absolutely hampered in my plans; and now, at
the close of April, I find myself placed in such a position through your continual
persecution that I am in positive danger of losing my liberty. The situation is
becoming an impossible one.'
"'Have you any suggestion to make?' I asked.
"'You must drop it, Mr. Holmes,' said he, swaying his face about. 'You really
must, you know.'"
--END--

[Web 964pts (44 solves)]Custom Location

Try to find out the database credentials.

The author changed the location of some files to protect the web application from script kiddies.

Link

Author:TheEmperors

As I tried to access to the path such as https://web0.ctfsecurinets.com/hoge, the debug mode 404 page was shown. It had the function to see the source code. I saw the config/bootstrap.php and found that the env file was /secret_ctf_location/env. So I also saw it from this link: https://web0.ctfsecurinets.com/_profiler/open?file=secret_ctf_location/env. The flag was Securinets{D4taB4se_P4sSw0Rd_My5qL_St0L3n}.

[Crypto 962pts (50 solves)]MAZE

we've intercepted a message that we want to decrypt! but we couldn't find the key! can u help ?

Author : KERRO

The zip file was provided. It included 101 RSA public key files and cipher.txt. My teammate st98 found there were two keys whose gcd was not 1. This fact implied that two keys had the same prime, namely n1 = p * q1 and n2 = p * q2. Since gcd(n1, n2) is p, it can be factorized. The following script decrypted the cipher.txt.

from Crypto.PublicKey import RSA
from Crypto.Util.number import *
from base64 import b64decode
import fractions


n1 = 23489536169894875104380319731091524594886928982079989680685140526506816078328377365920912691345973867274127369888617266471562817001630072737234558761977577075038991165371860879920389022518216143091522409148685349425962144785062882226489262290433631048454159440985148197906498710065684391869085391454754106309032049154232850533299594084654876542951785594662744724164946205787746362648504615426049549895862885960678401490612936954071297209796703651121038358809482596283354654704634698014090659491829169711549185505494879482606511678080905510586288124335730218605304747220982982161819303534160762335592895244302851836981
n2 = 23260716557375690457724340517814798553284093383675955753210132374509973258283306822538580132700875733321460692101694179361245119306418651193402512612500111442624876103473535129327037097046343634997541541496318494961543987552228754832236044259856000320983983126129412431801144031562287397570846392952905862981221569085591696654562289384531854207702626277469166158114197478525681143466552721501485707709954322005798540444098755630199517759570465455471064172816576843800829708415595842816666071002921806841594943525213770657127124965423586840200929234600612382079511164305586033156711399178225762365306090386434107266037
p = 168736462866079204789584058199620418225526506348478399447895958829038354002285480486576728303147995561507709511105490546002439968559726489519296139857978907240880718150991015966920369123795034196767754935707681686046233424449917085120027639055362329862072939086970892956139024391556483288415208981014264336691
q1 = 139208418683860744636489594107518498051692876942105482068436575406002091300025595750940476658875774324613311765708231971440632450100860632595797604226237831396754383891914573698131769762436941837224713009721577421233571830899874638297795728204831707647487557389464078420524002550428515370686466308350190419191
q2 = 137852341825115687630372545540234125575043643297743715914372468140743017665122928684176567678186288631724569119078697598260381091322877334920545216266418987980787824645288016924652387350842372904949656504253960440751217710040954075691996893620788950284865727480192510130305432030965910622333944701850333681207
assert(n1 == p * q1)
assert(n2 == p * q2)
phi1 = (p-1) * (q1-1)
phi2 = (p-1) * (q2-1)
e = 65537
d1 = inverse(e, phi1)
d2 = inverse(e, phi2)

C = int(open("cipher.txt", "r").read().strip())

print(long_to_bytes(pow(C, d1, n1)))
print(long_to_bytes(pow(C, d2, n2)))

The flag was securinets{rs4_1s_g00d_s0m3t1m3s!}

[Rev 919pts (65 solves)]AutomateMe

Huuuuge!

Authors : KERRO && Anis_Boss

64-bit ELF file was given. It was a simple crackme challenge. It had so many tiny functions that IDA couldn't show the graph, however, every function was pretty simple and had the same structure. I wrote the following script to recover the correct input.

import re


lines = open("dis.asm").read().splitlines()
i = 0
flagbuf = ['' for _ in range(0x1000)]
while i < len(lines):
    if lines[i] == 'mov    rax,QWORD PTR [rbp-0x20]':
        i += 3

        index = 0
        if lines[i].startswith('add'):
            r = re.findall(r',(.+)$', lines[i])
        else:
            r = re.findall(r'0x(.+)\]', lines[i])
        if len(r) == 1:
            index = int(r[0], 16)

        i += 1
        if lines[i].startswith('cmp'):
            pass
        else:
            i += 1
            if lines[i].startswith('cmp'):
                r = re.findall(r'0x(.+)$', lines[i])
                print(r[0], lines[i])
                flagbuf[index] = chr(int(r[0], 16))
            else:
                r = re.findall(r',0x(.+)$', lines[i])
                xor = r[0]
                i += 1
                r = re.findall(r',0x(.+)$', lines[i])
                c = r[0]
                flagbuf[index] = chr(int(xor, 16) ^ int(c, 16))
    else:
        i += 1
print(''.join(flagbuf))

The flag was included in the correct input securinets{automating_everything_is_the_new_future}.

[Rev 1000pts (7 solves)]Monster

Just give me the flag!

nc 54.87.182.197 1337

Author : KERRO

We were given 32-bit ELF file and dummy flag.txt. If we could input the correct value, the binary would execute cat flag.txt.

I reversed it using IDA's graph view, ghidra's decompiler, and gdb-peda. As I analysed the binary, I found the following facts:

  • the input format is %llx which means it's like d3adb33f
  • it calculate some hash value of tsebehtsignisrever using input as the seed and compare it to ca 3d 3b 5b 4c 9d d2 cb dd 17 8d dc b9 49 3b ea 12 25
  • the seed is used byte to byte

I focused on the last fact. It was possible to give all 256 patterns to the binary and get the first byte of correct seed. The following command did it.

$ for i in `seq 255`; do printf $i; printf '%02x\n' $i | ltrace ./rev 2>&1; done > trace

The first byte was 0xbe and nad the next byte could be found in the same way, which was 0xfe. The correct seed was febe and got the flag securinets{mD5_EncOd1nG_iS_Be4uT1fuL}. It was first blood.

UTCTF Quals 2019 Writeup

I participated in the UTCTF Quals 2019 as a member of a team insecure. We reached 18th place with 8250points. Many interesting challenges were provided in the competition. Thanks to all the admins! I'm writing up some challenges which I solved.

[Cryptography 200pts][basics]crypto

description:

Can you make sense of this file?

by balex

We were given a file: binary.txt which had many binary-formed integers.

01010101 01101000 00101101 01101111 01101000 00101100 00100000 01101100 01101111 01101111 01101011 01110011 00100000 01101100 01101001 01101011 01100101 00100000 01110111 01100101 00100000 01101000 01100001 01110110 01100101 00100000 01100001 01101110 01101111 01110100 01101000 01100101 01110010 00100000 01100010 01101100 01101111 01100011 01101011 00100000 01101111 01100110 00100000 01110100 01100101 01111000 01110100 00101100 00100000 01110111 01101001 01110100 01101000 00100000 01110011 01101111 01101101 01100101 00100000 01110011 01101111 01110010 01110100 00100000 01101111 01100110 00100000 01110011 01110000 01100101 01100011 01101001 01100001 01101100 00100000 01100101 01101110 01100011 01101111 01100100 01101001 01101110 01100111 00101110 00100000 01000011 01100001 01101110 00100000 01111001 01101111 01110101 00100000 01100110 01101001 01100111 01110101 01110010 01100101 00100000 01101111 01110101 01110100 00100000 01110111 01101000 01100001 01110100 00100000 01110100 01101000 01101001 01110011 00100000 01100101 01101110 01100011 01101111 01100100 01101001 01101110 01100111 00100000 01101001 01110011 00111111 00100000 00101000 01101000 01101001 01101110 01110100 ...

I decoded them to a sequence of characters and I got a base64 encoded string, which was the next part of this challenge.

Uh-oh, looks like we have another block of text, with some sort of special encoding. Can you figure out what this encoding is? (hint: if you look carefully, you'll notice that there only characters present are A-Z, a-z, 0-9, and sometimes / and +. See if you can find an encoding that looks like this one.)
TmV3IGNoYWxsZW5nZSEgQ2FuIHlvdSBmaWd1cmUgb3V0IHdoYXQncyBnb2luZyBvbiBoZXJlPyBJdCBsb29rcyBsaWtlIHRoZSBsZXR0ZXJzIGFyZSBzaGlmdGVkIGJ5IHNvbWUgY29uc3RhbnQuIChoaW50OiB5b3UgbWlnaHQgd2FudCB0byBzdGFydCBsb29raW5nIHVwIFJvbWFuIHBlb3BsZSkuCmt2YnNxcmQsIGl5ZSdibyBrdnd5Y2QgZHJvYm8hIFh5ZyBweWIgZHJvIHBzeGt2IChreG4gd2tpbG8gZHJvIHJrYm5vY2QuLi4pIHprYmQ6IGsgY2VsY2RzZGVkc3l4IG1zenJvYi4gU3ggZHJvIHB5dnZ5Z3N4cSBkb2hkLCBTJ2ZvIGRrdW94IHdpIHdvY2NrcW8ga3huIGJvenZrbW9uIG9mb2JpIGt2enJrbG9kc20gbXJrYmttZG9iIGdzZHIgayBteWJib2N6eXhub3htbyBkeSBrIG5zcHBvYm94ZCBtcmtia21kb2IgLSB1eHlneCBrYyBrIGNlbGNkc2RlZHN5eCBtc3pyb2IuIE1reCBpeWUgcHN4biBkcm8gcHN4a3YgcHZrcT8gcnN4ZDogR28gdXh5ZyBkcmtkIGRybyBwdmtxIHNjIHF5c3hxIGR5IGxvIHlwIGRybyBweWJ3a2QgZWRwdmtxey4uLn0gLSBncnNtciB3b2t4YyBkcmtkIHNwIGl5ZSBjb28gZHJrZCB6a2Rkb2J4LCBpeWUgdXh5ZyBncmtkIGRybyBteWJib2N6eXhub3htb2MgcHliIGUsIGQsIHAsIHYgaywga3huIHEga2JvLiBJeWUgbWt4IHpieWxrbHZpIGd5YnUgeWVkIGRybyBib3drc3hzeHEgbXJrYmttZG9iYyBsaSBib3p2a21zeHEgZHJvdyBreG4gc3hwb2Jic3hxIG15d3d5eCBneWJuYyBzeCBkcm8gT3hxdnNjciB2a3hxZWtxby4gS3h5ZHJvYiBxYm9rZCB3b2RyeW4gc2MgZHkgZWNvIHBib2Flb3htaSBreGt2aWNzYzogZ28gdXh5ZyBkcmtkICdvJyBjcnlnYyBleiB3eWNkIHlwZG94IHN4IGRybyBrdnpya2xvZCwgY3kgZHJrZCdjIHpieWxrbHZpIGRybyB3eWNkIG15d3d5eCBtcmtia21kb2Igc3ggZHJvIGRvaGQsIHB5dnZ5Z29uIGxpICdkJywga3huIGN5IHl4LiBZeG1vIGl5ZSB1eHlnIGsgcG9nIG1ya2JrbWRvYmMsIGl5ZSBta3ggc3hwb2IgZHJvIGJvY2QgeXAgZHJvIGd5Ym5jIGxrY29uIHl4IG15d3d5eCBneWJuYyBkcmtkIGNyeWcgZXogc3ggZHJvIE94cXZzY3Igdmt4cWVrcW8uCnJnaG54c2RmeXNkdGdodSEgcWdmIGlzYWsgY3RodHVpa2UgZGlrIHprbnRoaGt4IHJ4cWxkZ254c2xpcSByaXN5eWtobmsuIGlreGsgdHUgcyBjeXNuIGNneCBzeXkgcWdmeCBpc3hlIGtjY2d4ZHU6IGZkY3lzbnszaHJ4cWxkMTBoXzE1X3IwMHl9LiBxZ2YgdnR5eSBjdGhlIGRpc2QgcyB5Z2QgZ2MgcnhxbGRnbnhzbGlxIHR1IHBmdWQgemZ0eWV0aG4gZ2NjIGRpdHUgdWd4ZCBnYyB6c3V0ciBiaGd2eWtlbmssIHNoZSB0ZCB4a3N5eXEgdHUgaGdkIHVnIHpzZSBzY2RreCBzeXkuIGlnbGsgcWdmIGtocGdxa2UgZGlrIHJpc3l5a2huayE=

I decoded it and I found the last part of the output was encrypted with the substitution cipher.

New challenge! Can you figure out what's going on here? It looks like the letters are shifted by some constant. (hint: you might want to start looking up Roman people).
kvbsqrd, iye'bo kvwycd drobo! Xyg pyb dro psxkv (kxn wkilo dro rkbnocd...) zkbd: k celcdsdedsyx mszrob. Sx dro pyvvygsxq dohd, S'fo dkuox wi wocckqo kxn bozvkmon ofobi kvzrklodsm mrkbkmdob gsdr k mybboczyxnoxmo dy k nsppoboxd mrkbkmdob - uxygx kc k celcdsdedsyx mszrob. Mkx iye psxn dro psxkv pvkq? rsxd: Go uxyg drkd dro pvkq sc qysxq dy lo yp dro pybwkd edpvkq{...} - grsmr wokxc drkd sp iye coo drkd zkddobx, iye uxyg grkd dro mybboczyxnoxmoc pyb e, d, p, v k, kxn q kbo. Iye mkx zbylklvi gybu yed dro bowksxsxq mrkbkmdobc li bozvkmsxq drow kxn sxpobbsxq mywwyx gybnc sx dro Oxqvscr vkxqekqo. Kxydrob qbokd wodryn sc dy eco pboaeoxmi kxkvicsc: go uxyg drkd 'o' crygc ez wycd ypdox sx dro kvzrklod, cy drkd'c zbylklvi dro wycd mywwyx mrkbkmdob sx dro dohd, pyvvygon li 'd', kxn cy yx. Yxmo iye uxyg k pog mrkbkmdobc, iye mkx sxpob dro bocd yp dro gybnc lkcon yx mywwyx gybnc drkd cryg ez sx dro Oxqvscr vkxqekqo.
rghnxsdfysdtghu! qgf isak cthtuike dik zknthhkx rxqldgnxsliq risyykhnk. ikxk tu s cysn cgx syy qgfx isxe kccgxdu: fdcysn{3hrxqld10h_15_r00y}. qgf vtyy cthe disd s ygd gc rxqldgnxsliq tu pfud zftyethn gcc ditu ugxd gc zsutr bhgvykenk, she td xksyyq tu hgd ug zse scdkx syy. iglk qgf khpgqke dik risyykhnk!

The online solver could solve it. The flag was utflag{3ncrypt10n_15_c00l}.

[Forensics 100pts][basics]forensics

description:

My friend said they hid a flag in this picture, but it's broken!

by balex

We were given a file which had an extension .jpeg, however, it was the ascii text utflag{d0nt_tru5t_f1l3_3xt3ns10n5}.

[Web 650pts]HabbyDabby's Secret Stash

description:

HabbyDabby's hidden some stuff away on his web server that he created and wrote from scratch on his Mac. See if you can find out what he's hidden and where he's hidden it!

http://a.goodsecurity.fail/

by copperstick6

This challenge's page had an LFI vulnerability. The query http://a.goodsecurity.fail/?file=/etc/passwd showed the contents of /etc/passwd. As the challenge description said that HabbyDabby used a Mac machine to develop the page, I tried reading the file .DS_Store, which is usucally created by the macOS automatically. It had the information about the directory structure. I used this library to view them. I explored some directories and found the flag at http://a.goodsecurity.fail/e/d/e/flag.txt. By the way, the LFI vulnerability wasn't necessary to solve this challenge. What did that mean?

[Reverse Engineering 400pts]Domain Generation Algorithm

description:

This executable calculates a new domain depending on the system time. See if you can predict what the new domain will be on Tue, 20 Apr 2021 13:25:03 GMT.

Note: the flag is utflag{domain_name.tld}

by jitterbug_gang

The given file dga was an ELF. It was created with pyinstaller. Pyinstaller stores compiled bytes in the ELF file. So we can extract it using archive_viewer.py.

By the way, the byte-compilation of python(CPython) has 3 steps. The first step is the conversion from source code to bytecode. We can use the builtin compile function for this. The next step is to marshal the bytecode, this step corresponds to marshal.dumps function. The last step is to create the .pyc format data. The structure of .pyc is magic + timestamp + padding + marshaled-bytecode. The bytes which we can extract from the ELF are the marshaled-bytecode.

I wrote the following script for formatting the extracted bytecode to .pyc format and making pycdc decompile it to a source code.

import marshal
import struct
import time
import imp
import sys
import os

with open(sys.argv[1], 'rb') as f:
    code = f.read()

t = struct.pack('i', int(time.time()))
padding = b'A\x00\x00\x00'
# bytecode = marshal.dumps(code)
bytecode = code

with open(sys.argv[1]+".pyc", 'wb') as f:
    f.write(imp.get_magic())
    f.write(t)
    f.write(padding)
    f.write(bytecode)

I could restore the original source code successfully. My last work was to modify the code so that it prints the generated domain and uses a specified time.

# Source Generated with Decompyle++
# File: dga.pyc.pyc (Python 3.6)

from nistbeacon import NistBeacon
import time
from datetime import datetime
import hashlib

def gen_domain():
    # now = int(time.time())
    now = datetime.strptime('2021-04-20 13:25:03 +0000', '%Y-%m-%d %H:%M:%S %z').timestamp()
    now -= 100000000
    record = NistBeacon.get_previous(now)
    val = record.output_value
    h = hashlib.md5()
    h.update(str(val).encode('utf-8'))
    res = h.digest()
    domain = ''
    for ch in res:
        tmp = (ch & 15) + (ch >> 4) + ord('a')
        if tmp <= ord('z'):
            domain += chr(tmp)
    domain += '.org'
    return domain

if __name__ == '__main__':
    print(gen_domain())

The flag was: yervwuusmmiis.org.

[Reverse Engineering 750pts]simple python script

description:

simple python script I wrote while not paying attention in graphics

by asper

We were given a python script whose name was wtf.py. It was obfuscated as shown below. Wow there are cats!

flag = input("> ")
for i in range(0, len(flag), int((544+5j).imag)):
    inputs = []
    (lambda ねこ, _, __, ___, ____, _____, ネコ, q, k: getattr(ねこ, "extend")([(lambda _, __, ___: _(_, __, ___))(lambda _, __, ___:bytes([___ % __]) + _(_, __, ___ // __) if ___ else(lambda: _).__code__.co_lnotab,(_ << k),(((q << ネコ) - _____) << ((((_____ << __) - _) << ____) + _____)) + (((q << ネコ) - ((___ << __) + _)) << ((((_____ << __) - _) << ____) - q))...
    getattr(temp, "update")(getattr(flag[i:i + 5], "encode")("utf-8"))
    if getattr(__import__("difflib"), "SequenceMatcher")(None, getattr(getattr(temp, "hexdigest")(), "lower")(), getattr(inputs[i // 5], "decode")("utf-8").lower()).ratio() != 1.0:
        exit()

print("correct")

I think the print debug is effective in this sort of challenges. I categorized the nested lambda expression into 2 parts: the function-call part and the arguments-prepare part. Then I understood the work of this code. I simplified the obfuscated code into the following script.

s = input()
hashes = ['26d33687bdb491480087ce1096c80329aaacbec7', '1c3bcf656687cd31a3b486f6d5936c4b76a5d749', '11a3e059c6f9c223ce20ef98290f0109f10b2ac6', '6301cb033554bf0e424f7862efcc9d644df8678d', '95d79f53b52da1408cc79d83f445224a58355b13']
for i in range(0, len(s), 5):
  if sha1(s[i:i+5]).hexdigest() != hashes[i//5]:
    print("wrong")
    exit()
print("correct")

I found that the input string was checked in sha1 every 5-byte block. To find the correct input I cracked these hashes. Because the original text were just 5 characters long, bruteforce worked. The flag was puppyp1zzaanimetoruskitty.

[Reverse Engineering 1200pts]Crackme

description:

Attention all UTCTF players, asper is in great danger, and he needs YOUR help to reverse engineer this binary and figure out the password. To do this, he needs IDA Pro and a couple of breakpoints. To help him, all he needs is your credit card number, the three numbers on the back, and the expiration month and date. But you gotta be quick so that asper can secure the flag, and achieve the epic victory R O Y A L.

Note: the flag is the password with utflag{} wrapped around it.

by jitterbug_gang

Also, this binary was compiled a little differently, and you may need to install some extra dependencies to run it. (Or you can try solving this with just static analysis.) Try installing libc++abi-dev and libcxxtools-dev to run this challenge.

If that doesn't work for you, try to preload this libc file with LD_PRELOAD.

At first I replaced __libc_csu_init with nop because ptrace prevented me from debugging dynamically. I used radare2 for this work.

By analysing the binary dynamically & statically, I found that the binary was working like the following C code. In fact, this binary used an exception for jumping to somewhere in order to make it complicated. Also the loop I commented stuff was constructed on the memory at runtime.

char test[] = {...};
int main() {
    char buf[0x40];
    fgets(buf, 0x40, stdin);
    buf[0x34] ^= 0x43;
    buf[0x2f] ^= 0x44;
    for (int i = 0; i < strlen(buf); i++) {
        buf[i] ^= 0x27;
    }
    for (int i = 0; i < strlen(buf); i++) { // stuff
        buf[i] ^= (i + 0x33);
    }
    if (memcmp(buf, test, 0x40) == 0) {
        printf("correct\n");
    } else {
        printf("wrong\n");
    }
}

The correct input could be calculated by the following script.

data = [0x5, 0x4C, 0x7A, 0x70, 0x66, 0x2C, 0x41, 0x2C, 0x72, 0x7D, 0x2A, 0x6B, 0x75, 0x6, 0x12, 0x54, 0x54, 0xD, 0x3D, 0x15, 0x8, 0xE, 0x1A, 0x32, 0x1B, 0x5A, 0x6, 0x5, 0x37, 0x1B, 0x13, 0x14, 0x10, 0x2C, 0x6, 0x41, 0x2F, 0xB, 0x16, 0x4E, 0x23, 0x1A, 0x8, 0xB, 0x4B, 0x34, 0x32, 0x5E, 0x74, 0x25, 0x1D, 0x22, 0x33, 0x3F, 0x3E, 0x7E, 0x3E, 0x38, 0x3E, 0x20, 0x2B, 0x3C, 0x60]

for i in range(0, len(data)):
    data[i] = data[i] ^ (i + 0x33)

for i in range(0, len(data)):
    data[i] = data[i] ^ 0x27

data[0x34] = data[0x34] ^ 0x43
data[0x2f] = data[0x2f] ^ 0x44

print(repr("".join(map(chr, data))))

The result was \x11_hav3_1nf0rmat10n_that_w1ll_lead_t0_th3_arr3st_0f_c0pp3rstick6. The first character seemed wrong, but easy to guess the correct value was 1. The correct input was 1_hav3_1nf0rmat10n_that_w1ll_lead_t0_th3_arr3st_0f_c0pp3rstick6 and the flag was utctf{1_hav3_1nf0rmat10n_that_w1ll_lead_t0_th3_arr3st_0f_c0pp3rstick6}.

AeroCTF Quals 2019 Writeup

I participated in an AeroCTF Qual 2019 as a member of team insecure. We reached 14th place with 3411pts. Thanks to admins for a great competition.

I solved the following challenges. I'm going to describe them.

[Warmup 100pts(23 solves)]misc_warmup

description:

Our reverser loves to create various tasks. This is an example, try to solve.

We were given a file whose name was some_idb.i64. It was generated by IDA. So I opened it using IDA and viewed comments by shortcut key C-m. Then I saw the split flag.

f:id:Furutsuki:20190309224046p:plain

[Warmup 100pts(47 solves)]reverse_warmup

description:

Again, our developers are watching all kinds of memes. Now use for this some program. Try to crack this soft.

The given file just_a_meme.exe is a PE32 file which was written by Go. I analyzed it statically using IDA disassembler. Then I found that the main_checkKey method is the core of its work. It compares a static bytes \x89w\x86\x87\xa0\x8dw\x85\x8d\x85\x8d\xa3 with the command line argument character by character, where each character is xored with 0xd and added by 0x25.

rdata = [0x89, 0x77, 0x86, 0x87, 0xA0, 0x8d, 0x77, 0x85, 0x8d, 0x85, 0x8d, 0xa3]
key = ''

for x in rdata:
    key += chr( (x - 0x25) ^ 0xD )
print(repr(key))

I entered the key i_love_memes to the binary and got the flag.

[Crypto 444pts (33 solves)]pycryptor

description:

Our programmers decided not to use that encoder written in Golange and created a new one in Python. Needless to say that we again lose our drawings? Could you look again at the encoder and an example of its use?

We were given two files: cryptor.py(here) and enc_image.png. At first glance, I found that this image was xored. As some blocks in the image were encrypted with the same key, we could remove the effect of xoring from the image. I choosed the most common block as the base image and got the "nearly decrypted" image. The flag is written at the center-bottom of the decrypted image.

f:id:Furutsuki:20190309230610p:plain

f:id:Furutsuki:20190309230619p:plain

[Forensics 470pts(21 solves)]undefined protocol

description:

We managed to get traffic from the machine of one of the hackers who hacked our navigation systems, but they use some kind of strange protocol over TCP. We were not able to disassemble it, maybe you can find out what he was transmitting?

We got the undefiend_protocol_traffic.pcapng which had some unknown-formatted TCP traffics. By the observation, I could guess the protocol.

I saved the pcapng file as pcap format and decoded the messages by the following script.

f:id:Furutsuki:20190309231249p:plain

import dpkt
import re

def dec(k, s):
    r = ''
    for c in s:
        r += chr(ord(c) ^ k)
    return r

f = open("hoge.pcap", "rb")
pcr = dpkt.pcap.Reader(f)

key = None
for t, buf in pcr:
    eth = dpkt.ethernet.Ethernet(buf)
    ip = eth.data
    if isinstance(ip.data, dpkt.tcp.TCP):
        tcp = ip.data
        if len(tcp.data) != 0:
            d = tcp.data.decode()
            if re.match(r'[0-9]+\n', d):
                key = int(d[:-1])
            else:
                print(dec(key, d)[::-1])

There was a flag at the last of traffic.

(...snip...)
iloveyou
Username: 
lol
[-] Username is invalid!
Exit

qwerty1234
Username: 
admin
Enter the password: 
admin
Welcome admin!

Aero{94d04d04b327e4e52a0bb6c67b3fca7b}