记一次攻防演练

VSole2021-11-16 13:02:15

一、简介:

领导通知,让我打十天攻防,前四天,平平无奇,两个权限,web系统都是外包的,没打进核心内网。

这次攻防,没有给靶标,也没有给资产,全靠自己进行信息搜集。

由于本人不会钓鱼,所以只能打打外网了。

二、前期信息搜集

信息搜集主要以厂商系统为主,通过使用fofa,云悉,查ICP备案,APP脱壳逆向,公众号接口,小程序,天眼查查母公司以及子公司的备案和资产信息。通过能直接获取到的资产信息,进行二次信息搜集,主要是以C段,B段和目录扫描为主。

三、第五六七天

3.1 测试网站的功能点,想办法黑盒获取权限。

第五天主要是以代码审计为主。

通过信息找到找到子公司的一个备案网站系统。

根据左上角的功能提示,发现网站存在登录和注册功能,因此尝试注册一个账号。

点击注册按钮,发现跳转到登录界面。很奇怪,貌似注册功能无法正常使用。

f12查看源码发现端倪,注册相关的实现代码已经被注释掉了。

因此将注释符号删除,并使用注册功能注册了账号

此时使用注册功能成功注册了一个账号

并登录成功。

寻找上传接口,尝试文件上传。但是发现功能点似乎无法正常使用。

f12抓取上传接口的数据包。

访问该接口,上传表单成功出现。

尝试上传正常图片,均不能正常使用。提示都是文件大小不符合。

3.2 尝试利用TP框架漏洞获取权限

尝试尝试寻找后台,也没有找到。

尝试寻找SQL注入,没有找到。

此时发现该网站系统使用了thinkphp框架,但是具体不知道是哪个版本。

常用于获取tp框架版本的方法都是利用报错或者敏感文件,但是这里,似乎都没有。

盲打一波tp5的rce,均失败。

此时陷入瓶颈期。

想到了,该系统一定是基于tp框架开发的,但是具体是哪个CMS这里还未知,使用云悉获取该CMS信息也失败了。

3.3 尝试利用cms的已知漏洞获取网站权限和数据

此时无意间发现,上传接口的title中,泄露了该CMS信息,该cms为pigcms。

此时搜索有关该CMS的历史漏洞,通过cnvd平台。

尝试利用SQL注入漏洞,复现后均失败。

3.4 寻找CMS源码

注:此处找到的源码版本不一定会和目标站点一致。

尝试去寻找源码。官网看了一眼,真的贵。离谱。怎么可能花钱。

通过网盘搜索,百度搜索,谷歌搜索的方式,下载了源码。

本地环境搭建。

看着还挺像那么回事的。

确定后台路径。

本机后台

目标站点后台。

这后台长的不怎么像,影响不大。

试了一下初始密码,没进去。

看了一下后台,注入漏洞挺多的,有tp3.1的注入,也有pigcms的注入,也能文件上传GETSHELL,也能模板注入GETSHELL。

3.5 白加黑代码审计

3.5.1 前台任意文件上传GETSHELL

试了一下常规的未授权测试方法,均失败,因此只能考虑审计出前台漏洞了。这里用自己之前写的一个工具,遍历当前目录下指定后缀的文件路径。

将路径文件字典导入burpsuite的intruter模块的payload中,并去掉payload encoding前面的勾。


开始爆破。并根据response判断哪些文件是未授权访问的。

此时成功找到了未授权的入口文件。

并发现了两处关于文件上传的函数。

action_picUpload

public function action_picUpload(){    $error=0;    if (isset($_FILES['thumb'])){      $photo=$_FILES['thumb'];      if(substr($photo['type'], 0, 5) == 'image') {        switch ($photo['type']) {          case 'image/jpeg':          case 'image/jpg':          case 'image/pjpeg':            $ext = '.jpg';            break;          case 'image/gif':            $ext = '.gif';            break;          case 'image/png':          case 'image/x-png':            $ext = '.png';            break;          default:            $error=-1;            break;        }        if($error==0){          $time=SYS_TIME;          $year=date('Y',$time);          $month=date('m',$time);          $day=date('d',$time);          $pathInfo=upFileFolders($time);          $dstFolder=$pathInfo['path'];          $dstFile=ABS_PATH.'upload'.DIRECTORY_SEPARATOR.'temp'.$ext;          //the size of file uploaded must under 1M          if($photo['size']>2000000){            $error=-2;            return $error;          }        }else {          return $error;        }        //if no error        if($error==0){          $rand=randStr(4);                    //delete primary files                    if(file_exists($dstFolder.$time.$rand.$ext)){            unlink($dstFolder.$time.$rand.$ext);          }          if ($ext!='.gif'&&$ext!='.png'){            //save the temporary file             move_uploaded_file($photo['tmp_name'],$dstFile);            $imgInfo=getimagesize($dstFile);            //generate new files            $imageWidth=intval($_POST['width'])!=0?intval($_POST['width']):$imgInfo[0];            $imageHeight=intval($_POST['height'])!=0?intval($_POST['height']):$imgInfo[1];            bpBase::loadSysClass('image');            image::zfResize($dstFile,$dstFolder.$time.$rand.'.jpg',$imageWidth,$imageHeight,1|4,2);            $ext='.jpg';            //          }else {            move_uploaded_file($photo['tmp_name'],$dstFolder.$time.$rand.$ext);          }          if (isset($_POST['channelid'])){//内容缩略图            $channelObj=bpBase::loadAppClass('channelObj','channel');            $thisChannel=$channelObj->getChannelByID($_POST['channelid']);            $articleObj=bpBase::loadAppClass('articleObj','article');            $articleObj->setOtherThumb($thisChannel,$dstFile,$dstFolder,$time.$rand,'jpg');          }          if ($ext!='.gif'&&$ext!='.png'){            @unlink($dstFile);          }          $location='http://'.$_SERVER['HTTP_HOST'].CMS_DIR_PATH.'/upload/images/'.$year.'/'.$month.'/'.$day.'/'.$time.$rand.$ext;          $error=0;        }      }else {        $error=-1;      }    }else {      $error=-1;    }        if ($error==0){            echo $location;    }else {      $errors=array(-1=>'你上传的不是图片',-2=>'文件不能超过2M',-3=>'图片地址不正确');      echo $errors[intval($error)];    }  }

action_picUpload的逻辑是,上传的图片文件时,name=thumb,content-type的值为switch选择结构中的image/jpg时,指定上传后,文件的后缀名ext是jpg。文件名的命名是随机的,根据时间指定。

读懂逻辑后发现,此处的action_picUpload是无法上传文件获取权限的。

 继续审计第二次上传的函数。

action_flashUpload

阅读第二个上传函数的逻辑发现,当name的值是filepath,并且content-type的值是flash格式时,能够上传成功,上传后的后缀名是由filename的文件名后缀来确定的。

 构造文件上传的poc数据包

发现上传成功,回显php文件路径。

查看本地监听的文件路径生成情况,并确定php文件的最后路径。

访问后,phpinfo被成功执行。

尝试上传到目标站点,并上传成功。

此时跟队友分享喜悦,并准备周一打内网。

 由于和裁判沟通后,裁判要求,漏洞尽量要周一交。(意思是周末不攻防)

并得知提交0day漏洞是有额外加分。

3.5.2 蓝队周末居然上班

等到周六后,下午访问一下phpinfo看看。结果发现,蓝队居然上班了。phpinfo的页面内容变成了hack.

页面不是phpinfo?重新上传一下,好家伙,不讲武德,裁判都说休战了,你居然给我搞事情。

这是之前已经成功执行的截图。

离谱的一批。

继续审计

3.5.3 数据导出+可能的任意文件写入漏洞。

周日,开始重新审计。现在审计的思路主要是想办法拿到数据,并进入后台改配置,这样只要网站不关闭,我就有的是办法做webshell层面的权限维持,后面再做系统层面的权限维持。

private function export_database($tables,$sqlcompat,$sqlcharset,$sizelimit,$action,$fileid,$random,$tableid,$startfrom) {    $dumpcharset = $sqlcharset ? $sqlcharset : str_replace('-', '', DB_CHARSET);
    $fileid = ($fileid != '') ? $fileid : 1;        if($fileid==1 && $tables) {      if(!isset($tables) || !is_array($tables)) showMessage('请选择要备份的表');      $random = mt_rand(1000, 9999);      setCache('backupTables',serialize($tables));    } else {      if(!$tables = unserialize(getCache('backupTables'))) showMessage('请选择要备份的表');    }    if($sqlcharset) {      $this->db->query("SET NAMES '".$sqlcharset."';\n\n");    }        $tabledump = '';
    $tableid = ($tableid!= '') ? $tableid - 1 : 0;    $startfrom = ($startfrom != '') ? intval($startfrom) : 0;    for($i = $tableid; $i < count($tables) && strlen($tabledump) < $sizelimit * 1000; $i++) {      global $startrow;      $offset = 100;      if(!$startfrom) {        if($tables[$i]!=AUTO_TABLE_PREFIX.'session') {          $tabledump .= "DROP TABLE IF EXISTS `$tables[$i]`;\n";        }        $createtable = $this->db->query("SHOW CREATE TABLE `$tables[$i]` ");        $create = $this->db->fetch_next();        $tabledump .= $create['Create Table'].";\n\n";        $this->db->free_result($createtable);                      if($sqlcompat == 'MYSQL41' && $this->db->version() < '4.1') {          $tabledump = preg_replace("/TYPE\=([a-zA-Z0-9]+)/", "ENGINE=\\1 DEFAULT CHARSET=".$dumpcharset, $tabledump);        }        if($this->db->version() > '4.1' && $sqlcharset) {          $tabledump = preg_replace("/(DEFAULT)*\s*CHARSET=[a-zA-Z0-9]+/", "DEFAULT CHARSET=".$sqlcharset, $tabledump);        }        if($tables[$i]==AUTO_TABLE_PREFIX.'session') {          $tabledump = str_replace("CREATE TABLE `".DB_PRE."session`", "CREATE TABLE IF NOT EXISTS `".DB_PRE."session`", $tabledump);        }      }      $numrows = $offset;      while(strlen($tabledump) < $sizelimit * 1000 && $numrows == $offset) {        if($tables[$i]==AUTO_TABLE_PREFIX.'session') break;        $sql = "SELECT * FROM `$tables[$i]` LIMIT $startfrom, $offset";        $numfields = $this->db->num_fields($sql);        $numrows = $this->db->num_rows($sql);        $fields_name = $this->db->get_fields($tables[$i]);        $rows = $this->db->query($sql);        $name = array_keys($fields_name);        $r = array();        while ($row = $this->db->fetch_next()) {          $r[] = $row;          $comma = "";          $tabledump .= "INSERT INTO `$tables[$i]` VALUES(";          for($j = 0; $j < $numfields; $j++) {            $tabledump .= $comma."'".mysql_real_escape_string($row[$name[$j]])."'";            $comma = ",";          }          $tabledump .= ");\n";        }        $this->db->free_result($rows);        $startfrom += $offset;              }      $tabledump .= "\n";      $startrow = $startfrom;      $startfrom = 0;    }    if(trim($tabledump)) {      $tabledump = "# time:".date('Y-m-d H:i:s')."\n# bupu auto system:http://www.bupu.net\n# --------------------------------------------------------\n\n\n".$tabledump;      $tableid = $i;      $filename = date('Ymd').'_'.$random.'_'.$fileid.'.sql';      $altid = $fileid;      $fileid++;            $backUpFolder=ABS_PATH.DIRECTORY_SEPARATOR.'backup';      if (!file_exists($backUpFolder)&&!is_dir($backUpFolder)){        mkdir($backUpFolder,0777);      }      $bakfile_path = ABS_PATH.'backup'.DIRECTORY_SEPARATOR.'data'.date('Y-m-d',SYS_TIME);      if (!file_exists($bakfile_path)&&!is_dir($bakfile_path)){        mkdir($bakfile_path,0777);      }      $bakfile = $bakfile_path.DIRECTORY_SEPARATOR.$filename;      if(!is_writable($bakfile_path)) showMessage('backup文件夹不可写');      file_put_contents($bakfile, $tabledump);      @chmod($bakfile, 0777);      showmessage('正在备份,请不要关闭浏览器'." $filename ", '?m=manage&c=database&a=action_export&sizelimit='.$sizelimit.'&sqlcompat='.$sqlcompat.'&sqlcharset='.$sqlcharset.'&tableid='.$tableid.'&fileid='.$fileid.'&startfrom='.$startrow.'&random='.$random.'&allow='.$allow);    } else {      $bakfile_path = ABS_PATH.'backup'.DIRECTORY_SEPARATOR.'database';       //file_put_contents($bakfile_path.DIRECTORY_SEPARATOR.'index.html','');       delCache('backupTables');       showmessage('备份成功,数据备份在了“/backup/data'.date('Y-m-d',SYS_TIME).'”文件夹中');    }  }

通过阅读此处的代码逻辑,发现指定数据表名称,即可导出数据。

找到该sql文件路径。

前端访问并下载成功。

可以通过此方法,拿到后台管理员账号密码。

继续审计发现,此处的导出时,文件名可控,内容可控。

此时可以发现,可能可以截断后缀。

尝试截断,并成功。

尴尬的是,文件内容并没有写入。

查了相关资料后发现。

用冒号截断的确会这样,但是用windows文件流截断,文件也并没有生成。这就很麻烦了。

此处也没有想到比较好的方法去绕过。

就暂时放着了。

下午的时候尝试去下一下数据表的文件,结果发了几个数据包。

蓝队直接将admin.php这个入口文件给删了。牛逼牛逼。

跟队友说了一下情况。

3.5.4 蓝队不讲武德,直接关站

晚上准备写博客。

准备打开目标站点截几个图。

结果发现,蓝队直接给你关站了。笑死。

一片红,笑死我了,和队友吐槽大无语事件。

等周一明天举报了。

四、总结

缺乏攻防经验,没有在第一天拿到权限后,做权限维持。

五、后续

跟裁判反馈后,漏洞最后通过给了一百分,麻了。不过通过这次攻防增长了不少见识大概懂了一些恶心人的做法。

以后拿到权限,一定做好权限维持,不要相信裁判的鬼话,也不要信红蓝队会守规矩。


软件
本作品采用《CC 协议》,转载必须注明作者和本文链接
根据SecurityScorecard发布的《全球第三方网络安全漏洞报告》显示,2023年大约29%的违规行为可归因于第三方攻击媒介,因为许多违规行为的报告没有指定攻击媒介,所以实际比例可能要更高。MOVEit、CitrixBleed和Proself是2023年的软件供应链方面三个最广泛利用的漏洞,其中MOVEit零日漏洞产生广泛影响可能被归咎于第三方、第四方甚至第五方。
近日,以色列网络安全公司Seal Security宣布获得由Vertex Ventures Israel领投的740万美元种子轮融资,Seal归属软件供应链安全赛道,其研发的平台产品主要利用生成式AI为客户提供自动化的修复解决方案,其平均修复时间可从过去几个月缩短到现在的几个小时,足以以应对软件供应链这一日益严峻的挑战。
通过在开源软件包中插入恶意代码来迅速将恶意软件传播到整个软件供应链中是恶意分子常用的攻击手段。然而,最新的研究发现,如果用户等待大约14天后再将这些软件包更新到最新版本,就可以避免受到软件包劫持攻击的不良影响。
软件组成分析(SCA)应用程序安全测试(AST)工具市场的一个细分市场,负责管理开源组件的使用。SCA工具自动扫描应用程序的代码库,包括容器和注册表等相关构件,以识别所有开源组件、它们的许可证遵从性数据和任何安全漏洞。除了提供对开源使用的可见性之外,一些SCA工具还通过区分优先级和自动补救来帮助修复开源漏洞。SCA工具通常从扫描开始,生成产品中所有开源组件的清单报告,包括所有直接和传递依赖项。拥有
软件安全之CRC检测
2023-04-19 09:47:57
k++)//因为这里异或是从数据的高位开始,所以需要计算的数据左移8位,这里就需要计算8次。1)//判断最高位是否为1. 0xEDB88320;//最高位为1,右移一位,然后与0xEDB88320异或???相当于例子2中110与000异或值是不变的
基于各方在自身领域的专业积累,将此次调研工作进行了明确的分工,并将不定期进行调研分享交流会。
各类攻防演练的结果证明,软件供应链攻击已成为投入低、见效快、易突破的有效方式。总体思路与原则:合规是底线,管理是准则,制度是要求,技术是支撑,服务是保障,流程是协作。安全管理制度的建立,能够规范软件供应链涉及的内部、外部角色的行为,同时提供制度性保障。其次,针对软件开发各阶段与存在的风险,引入对应的安全能力,提供技术支撑,确保安全质量。
新推出的开放框架寻求为公司和安全团队提供全面且可行的方式深入了解软件供应链攻击行为及技术。这项名为开放软件供应链攻击参考(OSC&R)的计划由以色列软件物料安全管理公司OX Security主导,评估软件供应链安全威胁,覆盖一系列攻击途径,比如第三方库和组件漏洞、构建及开发系统供应链攻击,以及被黑或恶意软件更新包。
当下,软件开发安全的理念很火,各行各业都已认识到保障应用系统开发安全的重要性,但是要真正实现起来,结果却不是那么理想。
软件常见漏洞的解析
2022-11-28 10:16:06
理论基础漏洞可以定义为“在软件和硬件组件中发现的计算逻辑(例如代码)中的弱点,当被利用时,会对机密性,完整性
VSole
网络安全专家