<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    記一次攻防演練

    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
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类