磨刀不误砍柴工,有个好的审计工具对于代码审计是很重要的,工具用的好,往往可以使我们得心应手。我这里推荐的工具
vscode
,相信这个工具对于在读的各位并不陌生,
Visual Studio Code
(简称
VS Code
)是一个由微软开发,同时支持
Windows
、
Linux
和
macOS
等操作系统的免费代码编辑器,其强大之处主要在于该工具众多强大的插件。这里主要介绍两个,分别是
vscode-lua-format
和
lua
。
//post包POST /stok=89eb18a6314226c045d82175589ff4a1/ds HTTP/1.1Host: 192.168.0.1Content-Length: 132Origin: http://192.168.0.1Connection: closeReferer: http://192.168.0.1/{"method":"add","ipgroup":{"table":"rule_ipgroup","para":{"flag":"user","name":"aa","rule_scope":["—"],"comment":"aa","ref":"0"}}}//对应代码function index() register_module("ipgroup", "ipgroup") local e = require("luci.model.uci") local e = e.cursor() register_keyword_set_data("ipgroup", "rule_ipgroup", "rule_ipgroup_set_data") register_keyword_add_data("ipgroup", "rule_ipgroup", "rule_ipgroup_add_data") register_keyword_del_data("ipgroup", "rule_ipgroup", "rule_ipgroup_del_data")function rule_ipgroup_add_data(t, l, l, l) local l = p.cursor() local r = e.ipgroup_table_count_get_by_key_value("flag", "user") local l = l:get_profile("ipgroup", "group_max") or s if t.flag == "user" and r >= l then return n.ETABLEFULL end local l = e.ipgroup_table_entry_get_is_exist("name", t.name) if l ~= false then return n.EENTRYEXIST end t.secname = t.name t.flag = t.flag or "user" t.comment = t.comment or "" if t.comment ~= nil then t.comment = string.gsub(t.comment, "'", "''") end if t.rule_scope == nil then t.rule_scope = {} end if t.rule_ipgroup == nil then t.rule_ipgroup = {} end e.ipgroup_table_entry_insert(t) local _ = e.ipgroup_table_entry_get_is_exist("name", t.name, "id") local r = e.ipscope_table_entry_get_id_table_by_name(t.rule_scope) local l = {} for e = 1, #r do l[e] = r[e].id end if #l > 0 then e.relation_table_entry_multi_insert(_[1].id, l) end local r = e.ipgroup_table_entry_get_id_table_by_name(t.rule_ipgroup) local l = {} for e = 1, #r do l[e] = r[e].id end if #l > 0 then e.group_relation_table_entry_multi_insert(_[1].id, l) end ipgroup_after_proc("add") return n.ENONE, {["ipgroup"] = {["name"] = t.name}}end
由上面可以知道,
post
包中
json
数据的
method
参数的值可以是
add
、
set
、
del
,分别对应代码中的三个函数,接着
ipgroup
对应
register_keyword_set_data
函数的第一个参数,
table
的值对应第二个参数,第三个参数就是对应注册要执行的函数,函数传进的第一个参数就是对应后面一串
json
数据。
3.3 寻找危险函数
我们理清完接口的调用逻辑之后,现在就可以反向寻找脆弱点。先来找他的危险函数,跟审计伪
C
代码不一样的是,他不用考虑栈方面的漏洞。
function call(…) return u.execute(…) / 256 endfunction fork_exec(…) local e = n.fork() if e == 0 then n.chdir("/") n.chdir("/") local e = n.open("/dev/null", "w+") if e then n.dup(e, n.stderr) n.dup(e, n.stdout) n.dup(e, n.stdin) if e:fileno() > 2 then e:close() end end return n.exec("/bin/sh", "-c", …) else return endendfunction fork_call(…) local t = n.fork() if t == 0 then local e = n.open("/dev/null", "w+") if e then n.dup(e, n.stderr) n.dup(e, n.stdout) n.dup(e, n.stdin) if e:fileno() > 2 then e:close() end end return n.exec("/bin/sh", "-c", …) elseif t > 0 then local t, e, n = n.waitpid(t) if e == "exited" then return n else return nil end endendfunction exec(e) local e = r.popen(e) local n = e:read("*a") e:close() return nend
function upload_pic() local t = {} local l, i, _ local c = 0 local d = 200 * 1024 local r = 0 local a = "/www/web-static/resources/authserver/tmpfile" local h = PORTAL_TEMPLATE_DIR luci.http.setfilehandler(function(i, t, e) 参数i是个结构体,里面有文件名,文件大小等参数 if not l then l = io.open(a, "w") c = 0 end if t then c = c + #t if c <= d then l:write(t) else if 0 == r then l:close() o.fork_call("rm -f " .. a) l = io.open(a, "w") r = 1 end end end if e then l:close() end end) luci.http.formvalue("filename")
function upload_db() local _ = 131072 local t = 0 local i local t = nil local o = nil local l = 0 local n = e.ENONE luci.http.setfilehandler(function(r, a, s) if not i then if r then if r.name == "isp_database" then t = p o = ". /lib/isp_route/isp_route.sh && isp_route_restore_database" elseif r.name == "user_database" then t = d o = ". /lib/isp_route/isp_route.sh && isp_restore_user_database" end end if t == nil then n = e.EISPDBNULLFILENAME return end i = io.open(t, "w") l = 0 end if a then l = l + #a if l <= _ then i:write(a) else n = e.EISPDBTOOLARGE return end end if s then i:close() end end) luci.http.formvalue("trigger-parser")
isp_route_restore_database(){ if [ ! -f $ISP_DATABASE_TMP ];then return 1; fi /usr/sbin/isp_route_db system -c if [ $? != "0" ]; then logger -t isp -p warn "The isp system database's format is invalid." rm $ISP_DATABASE_TMP -f return 1; fi #move the tmp isp database to /etc/nouci_config/dbs mv $ISP_DATABASE_TMP $ISP_DATABASE_DIR -f state=`uci get isp_route.global.state 2>/dev/null` [ "$state" == "on" ] && /etc/init.d/isp_route stop /usr/sbin/isp_route_db system -b if [ "$state" == "on" ]; then /etc/init.d/isp_route start else for i in $isp_tables; do ipset destroy $i 2>/dev/null done fi}isp_restore_user_database(){ if [ ! -f $ISP_USER_DB_TMP ];then return 1; fi #check the valid of user's database /usr/sbin/isp_route_db user -c if [ $? != "0" ]; then logger -t isp -p warn "The isp user database's format is invalid." rm $ISP_USER_DB_TMP -f return 1; fi #move the tmp isp database to /etc/nouci_config/dbs mv $ISP_USER_DB_TMP $ISP_USER_DB_DIR -f state=`uci get isp_route.global.state 2>/dev/null` [ "$state" == "on" ] && /etc/init.d/isp_route stop /usr/sbin/isp_route_db user -b if [ "$state" == "on" ]; then /etc/init.d/isp_route start else ipset destroy ISP_USER_DEFINE 2>/dev/null fi}
//post包{"method":"do","login":{"username":"xxxxx","password":"xxxxx"}}//对应代码if n["query_auth_log"] then if n.method ~= "do" then r[e.NAME] = e.EINVARG write_json(r) return false end return action_get_unauth_log(n["query_auth_log"]) end if n["get_domain_array"] then if n.method ~= "do" then r[e.NAME] = e.EINVARG write_json(r) return false end return action_get_domain_array(n["get_domain_array"]) end local a, c = l.is_locked() if a then r[e.NAME] = e.EUNAUTH r.data = get_unauth_data(c) write_json(r) return false end if "" ~= i then luci.http.redirect("/") return false end if n.login then if n.method ~= "do" then r[e.NAME] = e.EINVARG write_json(r) return false end return action_login(n.login) end if (n["administration"] and n["administration"]["set_pwd_before_login"]) or n["set_password"] then if n.method ~= "do" then r[e.NAME] = e.EINVARG write_json(r) return false end if n["set_password"] then local e = n["set_password"]["username"] local t = n["set_password"]["password"] n["set_password"] = nil n["administration"] = {} n["administration"]["set_pwd_before_login"] = {} n["administration"]["set_pwd_before_login"]["username"] = e n["administration"]["set_pwd_before_login"]["password"] = t end return t.ds(n) end