Bỏ qua

non cai hand

Introduction

EHCTF

Category: Web

Write-up date: 03/03/2025

Question: PHP so suck :(

Source code

Point: 269 (nice)

Source code analysis

Vì đề bài không cho quá nhiều thông tin nên chúng ta sẽ lược qua phần phân tích đề bài và chú trọng dến source code. Thông qua source code ta có thể thấy phần so sánh của php được sử dụng là == (Loose Comparison). Vì php là một ngôn ngữ không yêu cầu về kiểu dữ liệu nên một số trường hợp so sánh sẽ tạo ra một lỗi không mong muốn. Lỗi này trong php được gọi là PHP Type Juggling

Dưới đay là bảng so sánh Loose comparison (Điều kiện để sảy ra Type Juggling)

Exploit

Đề bài yêu cầu chúng ta cung cấp 5 param gồm param từ một đến 5

$param1 = $_GET['param1'];
$param2 = $_GET['param2'];
$param3 = $_GET['param3'];
$param4 = $_GET['param4'];
$param5 = $_GET['param5'];

Chúng ta sẽ chia nhỏ thành từng so sánh một cho dễ theo dõi

Checkpoint 1

if (!($param1 == md5($param1))) {
    die('Try harder!');
} 

Nếu param1 != md5($param1) thì trả về giá trị là Try harder Ở param 1 này, mình sẽ dùng "Magic Hash", hay còn gọi là những hash đặc biệt. Những hash này sau khi qua hàm md5() sẽ có giá trị hex là 0e... . Khi đem đi so sánh những string đặc biệt có ký tự 0e ở đầu, php sẽ coi những sâu đấy là scientific notation và coi chúng là float. Khi đó hai float gần sấp sỉ nhau so sánh sẽ bằng nhau

Payload 1: param1=0e1137126905 hoặc param1=0e215962017

Checkpoint 2

if (!($param2 !== $param3 && hash('md5', $SALT . $param2) == hash('md5', $SALT . $param3))) {
    die('Try more harder!');
}

Đoạn này thì mình không hẳn là dùng bug của type juggling lắm mà mình dùng một unexpected behaviour khác. Khi mà một string concat (.) với một array thì giá trị trả về sẽ là string đó với Array, ví dụ như:

$arr = [1];
die('a' . $arr);

Khi concat như trên, hàm sẽ báo lỗi là Warning: Array to string conversion và trả giá trị aArray. Như vậy chỉ cần hai array khác nhau thì điều kiện trên sẽ đúng.

Payload 2: param2[]=1 và param3[]=2

Checkpoint 3

if (!(strstr($param4, 'fromehcwithlove') && strlen($param4) == 30)) {
    die('Try much more harder!');
}

Checkpoint này đơn giản hơn các checkpoin trên, ở đây chỉ cần đọc hiểu code và làm theo. Param4 cần chứa fromehcwithlove và param4 = 30 ký tự

Payload3: param4=fromehcwithlovefromehcwithlove

Checkpoint 4

if (!strcmp(FLAG, $param5)) {
    echo 'Here is your flag: ' . FLAG;
} else {
    die('One more!!!!!');
}

Lợi dụng việc strcmp và một số hàm khác khi so sánh với null sẽ trả về int(0) hay false ta có thể điền param là một array rỗng với không phần tử nào và strcmp() sẽ trả về giá trị là 0 và từ đó in ra cho chúng ta flag.

Ở đay có thể thấy $param4 được so sánh với flag qua hàm strcmp() Hàm sẽ trả về giá trị > 0 nếu string1 > string2 và 0 nếu hai string giống nhau. Chúng ta muốn ở đây là một giá trị > 0 nếu string

Payload 3: param5[]=

Sau khi hoàn thành đủ 5 param, chúng ta nhận được giá trị trả về là Here is your flag: EHCTF{Ju57_aN_3Asy_cHA1LENgE_RlgHt?_3df83c149031}

FLAG: EHCTF{SomeTIme5_EvERy7h1Ng_I5_EA5Y_7Han_y0u_ThlNK_:V_44380a08ef95}