安恒9月赛部分复现记录

时间:2024-04-11 09:38:24

前言

赶紧趁着电脑的屏幕修好了,刚好安恒的web题目有复现,赶紧做。。。。。又从这几个题目里面学习到新知识了小结一下

正文

web

babybypass

这个题目我记得我当初做的时候是一直考虑着用$以及_去绕过这些数字字母之类的东西,突然发现他这个题目里面把那个$以及_也经过了过滤,这样的话就少了很多可能性,而且这题比原题的限制长度小了,更有难度。现在就开始总结一下从这个题目学到的知识点
1.php短标签输出,这个知识点很久之前就已经见过。
<?=?>这个就是短标签,相当于<?php echo …;?>一般在php的配置文件php.ini中有一个short_open_tag的值,开启以后可以使用PHP的短标签:<? ?>,但是在现实开发中一般不推崇这种做法。。
但这个题目就很巧妙用了这个东西
2.第二个就是关于php函数eval函数的一些东西,这里的php执行函数是eval,在php官方手册上面有这样一句话,这是一个坑了,要想执行代码的话,我们就需要先用?>去闭合,把上一段代码就给结束离开 ,然后我们才可以利用<?=?>去输出
安恒9月赛部分复现记录

区别一下有些系统执行函数比如system(有回显),exec(没回显),以及反引号这类的,这是直接执行系统函数的,一会我们也会用到
安恒9月赛部分复现记录
安恒9月赛部分复现记录

3.Linux通配符的作用

 *        代表『 0 个或无穷多个』任意字符
 ?        代表『一定有一个』任意字符

4.Linux 下面一切皆文件
就是说我们平常一般在下面Linux下面的输入的命令,都可以通过文件去同样的执行,这对我来说是新知识。。。。。
安恒9月赛部分复现记录

然后我们就可以去实现payload,这个题目是Apache搭的,我们可以去默认目录先查看一波文件,一般来说默认目录在/var/www/html

构造code=?><?=`/???/??? /???/???/????/*`;?>相当于code=?><?=` /bin/cat /var/www/html/*`;?>

然后会发现php代码里面的源代码getflag那一部分的函数是是从/flag里面读取的。。。。听说这题的getflag函数也是个坑。。好像不能直接从url里面调用

所以再来构造一下
code=?><?=`/???/??? /????`;?>相当于code=?><?=` /bin/cat /flag`;?>得到flag
安恒9月赛部分复现记录

神奇的CMS

进入网站发现有用户登录,然后测试发现弱密码
admin admin123

然后点击几个模块发现有两个模块有点奇怪
安恒9月赛部分复现记录

这两个模块一个模块有提示,另一个模块存在输入并且存在回显
先看提示,一是让你下载源码包,另外是提示flag在哪,先把源码包下载下来,源码里面有两个控制器,一个是content,另一个是site

发现是Yii框架的东西,幸亏我还是做过Yii框架的开发的,对此还是有点小熟悉。。。

这里看回放的时候还是学到点东西的,就是我们在代码审计的时候一般要找有输入有输出的地方,这里很可能就存在这漏洞,这就相当于在脑里面建了一个模型,但是想想也很正常,平常我们学编程的时候也没过多的去考虑编程的输入输出,比如我们在一开始用C编程的时候你会忘掉除数不为0的前提吗?所以说我们更多的是考虑代码的核心功能能否实现。

所以在ADD_IMG(因为有输入有回显)页面里面就很有可能存在漏洞了,所以我们得专注代码里面描述这一段东西的内容

Yii框架讲究的是MVC架构,一般代码审计的话得先找Controller里面的东西毕竟都是些逻辑性的东西,关键的功能也在里面
所以在这个url里面的site是指的控制器,而backup就是site控制器里面的操作
安恒9月赛部分复现记录
我以前的博客讲过
安恒9月赛部分复现记录

从代码里面你会发现site控制器里面都是些展示页面以及登陆的功能,但是不要过分以为登录里面会有sql注入,毕竟Yii框架里面是可以通过对login函数设置rules再来通过validate去检测是否出现SQL注入,所以出现sql注入机率不大

但是在回放里面学到了一种更骚气的代码调试的方法。。就是当你不懂框架的时候如何将其变成普通的php代码进行调试
这个方法就是将跟框架有关的东西去掉把关于框架的类看成一个普通的类,再把跟业务逻辑以及数据库相关操作的功能有关的去掉,然后新建那个类,单纯测试一下那个输入输出的函数就好

尝试调试,我这里用的phpstorm去调试,在parstIf函数下了断点,发现执行到下图所示的地方就会重新返回到echo输出语句中
安恒9月赛部分复现记录
说明要在字符串里面加上{if此类的字符串
随便找一个地方加加,为的是能够进入else的循环
安恒9月赛部分复现记录
进入else会发现到buildregx又会有一个新的函数,其实这个函数就是为了构建一个正则表达式,在字符串前后添加/,可以从debug的结果看得出来
安恒9月赛部分复现记录
这时候我们就需要匹配/{if:(.*?)}(.*?){end if}/is这样的正则,但是之前的输入会导致其跳出该循环,从而又直接返回了结果,不能进入到for循环里面执行eval函数
安恒9月赛部分复现记录
这时候就又需要构建对应的东西了,我们把正则里面的(.*?)改为自己随意的内容就好
安恒9月赛部分复现记录
然后再追踪一次变化过程,经过preg_match_all这个函数的时候我们就可以看到这个函数会把原来的匹配的字符分成三段,首先是iar[0][0]是匹配到的字符串{if:2333}23333{end if},然后是分别是第一个任意内容以及第二个任意内容匹正则配到的字符串,iar[1][0]=2333,iar[2][0]=23333

继续走的话,这时候就可以进行一次循环了,因为现在arlen=1,然后我们就可以继续执行下去了,后买的事情就很简单了,先是判断iar[1][0]以及iar[2][0]里面是否有等号,再判断一下iar[2][0]里面是否有{elseif以及else,这几个判断都不影响
安恒9月赛部分复现记录
最后还是将iar[1][0]放在这个eval语句里面执行
安恒9月赛部分复现记录
梳理一下逻辑也就是说检查最少的是iar[1][0],只检查他是否存在=,那就好办了,在eval语句中我们只需要对iar[1][0]闭合一下语句我们就可以执行我们想要的命令即可

Payload
{if:1)print_r(`cat /tmp/flag`);die();//}123{end if}第一种思路直接在里面拼接命令执行语句即可
安恒9月赛部分复现记录

第二种思路就是在没有过滤$GOLBALS全局变量的前提下使用拼接,但个人感觉这东西会在PHP开发中给禁掉吧毕竟可能会对代码里面的其他变量造成影响,这个套路一般都是用来写过waf的小马用的

{if:1)$GLOBALS['_G'.'ET'][a]($GLOBALS['_G'.'ET'][b]);die();//}{end if}

尝试这个的是时候还以为服务器坏了。。。突然发现processing request。。。应该是没传参,传参了就好了

安恒9月赛部分复现记录

安恒9月赛部分复现记录
安恒9月赛部分复现记录

Crypto

简单加密

这个题目帮我复习了一下信息安全数学基础第一章的内容,所以我就记录一下这个题目,其他题目就不写了

这个关键点就是作取余运算的时候结果是不会超过余数的,如果有打过ACM的人就会知道有一个叫快速幂的东西跟这个也差不多,极大地降低了代码所需要的空间度,使得数不会越界

观察主函数现在我们需要的就是passwd这个参数,但是从generate_passwd是得到最多passwd不会超过0xB18E,所以我们就可以选择**
安恒9月赛部分复现记录

跑一下脚本就可以得到flag

#!/usr/bin/env python
# -*- coding:utf-8 -*- 
from Crypto.Cipher import AES
from Crypto import Random


def encrypt(data, password):
    bs = AES.block_size
    pad = lambda s: s + (bs - len(s) % bs) * chr(bs - len(s) % bs)
    iv = "0102030405060708"
    cipher = AES.new(password, AES.MODE_CBC, iv)
    data = cipher.encrypt(pad(data))
    return data
 
def decrypt(data, password):
    unpad = lambda s : s[0:-ord(s[-1])]
    iv = "0102030405060708"
    cipher = AES.new(password, AES.MODE_CBC, iv)
    data  = cipher.decrypt(data)
    return unpad(data)
    
def generate_passwd(key,result):
    data_halt = "LvR7GrlG0A4WIMBrUwTFoA==".decode("base64")
    rand_int =  int(decrypt(data_halt, key).encode("hex"),16)
    #round = 0x7DC59612
    result = result * (rand_int % 0xB18E) % 0xB18E
    return encrypt(str(result), key)
    

if __name__ == '__main__':

    key = '17abeca4cc4c432a52c2b7f6d24d1888'
    
    output = "u6WHK2bnAsvTP/lPagu7c/K3la0mrveKrXryBPF/LKFE2HYgRNLGzr1J1yObUapw"

    for result in range(0xB18E):
        passwd = generate_passwd(key.decode("hex"),result)
        r = decrypt(output.decode("base64"), passwd)
        if 'flag' in r:
            print r