这是一个非常简单的系统,基于发送似乎存在安全漏洞的 JSON 消息。有一个 Python 服务器(使用标准库中包含的 JSON 模块)接收 JSON 对象并对其进行操作。如果它得到{"req": "ping"}
,它只是返回{"resp": "pong"}
。它还有一个用于设置音量的命令,以及一个用于更改管理员密码的命令。管理员可以将任何 JSON 发送到此服务器。这是(server.py):
import json
import sys
def change_admin_password(p): pass # empty for test
def set_volume(v): pass # empty for test
def handle(js):
if (js["req"] == "ping"):
return {"resp": "pong"}
if (js["req"] == "change admin password"):
change_admin_password(js["args"]["password"])
return {"resp": "ok"}
if (js["req"] == "set volume"):
set_volume(int(js["args"]["volume"]))
return {"resp": "ok"}
print handle(json.load(sys.stdin))
它从标准输入读取命令并处理它。另一个脚本将从网络套接字读取 JSON 对象并将它们传递给该脚本。
其他用户必须通过使用libjson用 C++ 编写的代理。它只是阻止需要管理员权限的请求。例如,如果用户尝试更改管理员密码,代理将拒绝该命令:
$ echo '{"req": "change admin password", "args": {"password":"new"}}' | ./proxy
echo $?
1
这是代码(proxy.cpp):
#include "libjson.h"
using namespace std;
int main() {
string json((istreambuf_iterator<char>(cin)), istreambuf_iterator<char>());
JSONNode n = libjson::parse(json);
string req = n.at("req").as_string();
if (req == "change admin password") {
return 1;
}
cout << n.write();
}
要使用代理,管理套接字的主脚本将通过代理传输数据并将其输出到 Python 服务器:
$ echo '{"req": "ping"}' | ./proxy | python server.py
{'resp': 'pong'}
$ echo '{"req": "set volume", "args": {"volume": 50}}' | ./proxy | python server.py
{'resp': 'ok'}
如果用户尝试使用受限命令,它将按预期失败:
$ echo '{"req": "change admin password", "args": {"password": "new"}}' | ./proxy | python server.py
Traceback (most recent call last):
File "server.py", line 17, in <module>
[...]
但是由于某种原因,如果“req”键在 JSON 中出现两次(不应该是非法的吗?),非管理员用户可以更改管理员密码:
$ echo '{"req": "nothing", "req": "change admin password", "args": {"password": "new"}}' | ./proxy | python server.py
{'resp': 'ok'}
为什么?
尝试 Mike McMahon 的解决方法:
我尝试使用JSONNode.find
它作为解决方法,但它似乎不起作用。
我试着迭代所有元素:
#include "libjson.h"
using namespace std;
int main() {
string json((istreambuf_iterator<char>(cin)), istreambuf_iterator<char>());
JSONNode n = libjson::parse(json);
for (JSONNode::json_iterator it = n.begin(); it != n.end(); it++) {
cout << "found one: " << it->at("x").as_string() << endl;
}
}
这有效:
$ g++ proxy.cpp libjson.a -o proxy && ./proxy
In file included from libjson.h:4:0,
from proxy.cpp:1:
_internal/Source/JSONDefs.h:157:6: warning: #warning , Release build of libjson, but NDEBUG is not on [-Wcpp]
{"a": {"x": 1}, "b": {"x": 2}}
found one: 1
found one: 2
除了 JSON 无效时会出现段错误吗?我使用迭代器错了吗?
$ echo '{"a": {"x": 1}, {"b": {"x": 2}}' | ./proxy
found one: 1
Segmentation fault
我将其替换n.begin()
为n.find("y")
:
#include "libjson.h"
using namespace std;
int main() {
string json((istreambuf_iterator<char>(cin)), istreambuf_iterator<char>());
JSONNode n = libjson::parse(json);
for (JSONNode::json_iterator it = n.find("y"); it != n.end(); it++) {
cout << "found one: " << it->at("x").as_string() << endl;
}
}
它根本不起作用。我使用迭代器错了吗?
g++ proxy.cpp libjson.a -o proxy && ./proxy
In file included from libjson.h:4:0,
from proxy.cpp:1:
_internal/Source/JSONDefs.h:157:6: warning: #warning , Release build of libjson, but NDEBUG is not on [-Wcpp]
{"y": {"x": 1}}
Segmentation fault
解决方法的另一种尝试:
#include "libjson.h"
using namespace std;
int main() {
string json((istreambuf_iterator<char>(cin)), istreambuf_iterator<char>());
JSONNode n = libjson::parse(json);
for (JSONNode::json_iterator it = n.begin(); it != n.end(); it++) {
if (it->name() == "req" && it->as_string() == "change admin password")
{
cout << "found one " << endl;
}
}
}
有用!
$ echo '{"req": "change admin password"}' | ./proxy
found one
$ echo '{"req": "x", "req": "change admin password"}' | ./proxy
found one
$ echo '{"req": "change admin password", "req": "x"}' | ./proxy
found one
但是仍然存在无效 JSON 输入的段错误?
$ echo '{"req": "x", {"req": "x"}' | ./proxy
Segmentation fault