文章总结: 本文记录了对开源图库LightPicture的代码审计过程,发现了JWT密钥硬编码导致的任意用户登录漏洞及文件上传未校验后缀引发的Getshell漏洞。作者详细分析了ThinkPHP架构下的漏洞成因,复现了伪造管理员Token和上传Webshell的利用链。建议审计时重点关注认证授权与文件操作等高风险模块,以有效发现安全隐患。 综合评分: 86 文章分类: 代码审计,漏洞分析,WEB安全
代审0day-开源图库任意用户登录+getshell
原创
Yi1StaR Yi1StaR
奕星安全
2026年3月2日 11:02 广东
本篇文章仅用于技术交流,请勿利用文章内的相关技术从事非法测试,由于传播、利用本公众号所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责。
一、前言
本次是对开源项目LightPicture的代码审计过程记录,分析了两个比较严重的漏洞。本次审计在两个月前就完成了,但是一直没写文章记录。在今年年前发现ncc社区已经有其他师傅审计并发出了,自己就也在公众号记录一下。
二、项目架构分析
项目地址:https://github.com/osuuu/LightPicture
进去可以看见是thinkphp+vue开发的项目。
thinkphp的项目就属于是MVC架构。
包含 model、controller、services等等
三、任意用户登录
这是由于tokenkey硬编码导致token可以伪造,从而引发了任意用户登录的漏洞。
打开controller/Account.php查看login方法。
存在一个createToken的token生成函数。
$token = $tokenClass->createToken($user['id']);
跟进一下这个函数,看看是如何生成token的。
跟进到了app\services\AuthToken.php,可以找到这个方法。
class AuthToken{ /** * 获取token * @param string $username 自定义参数 */ public function createToken($username) { try { $key = TokenKey; $time = time(); //当前时间 //$token['iss']=''; //签发者 可选 //$token['aud']=''; //接收该JWT的一方,可选 $token['iat'] = $time; //签发时间 $token['nbf'] = $time; //(Not Before):某个时间点后才能访问,比如设置time+30,表示当前时间30秒后才能使用 $token['exp'] = $time + 85400*14; //token过期时间 $token['username'] = $username; //自定义参数 $json = JWT::encode($token, $key); return $json; } catch (\Firebase\JWT\ExpiredException $e) { //签名不正确 // return $this->create([], $e->getMessage(), 104); return ""; } catch (\Exception $e) { //其他错误 return ""; } } }
分析一下这个方法的逻辑:
该方法需要传入一个参数($username)。
1、通过全局常量 TokenKey获取密钥。
2、调用 time() 获取当前服务器时间戳。
3、设置签发时间、生效时间、过期时间;过期时间默认设置为14天。
4、接收传入的username的参数。
5、通过$json=JWT::encode($token, $key);方法生成token。
6、出现异常则均返回空字符串。
我们可以知道,仅需$username和key就可以得到token。
username是自己传入的,所以我们只需要知道key的值就可以得到token
我们全局搜索这个key常量,可以得到:
define("TokenKey", "e06b7b61978b30fd1e87723ef61746fb");
现在token生成的条件都满足了.
写一个php,来调用createtoken函数来生成一个管理员token。
当然可能会有人有疑惑,为什么这里username是1。
是因为 虽然AuthToken.php传入的是$username。
但是在Account.php生成的时候是传入的是$user[‘id’]生成的token。
而admin的id为1。所以需要设置为1。
拿前面得到的token去自己的网站验证一下。
f12 在本地存储添加刚刚生成的lp_token,即可进入管理员后台。
也可也成功调用/user/info接口,返回管理员信息。
四、任意文件上传getshell
查看controller/Api.php,可以看见upload(Request$request)上传方法。
查看路由route\app.php文件,可以发现Api.php不存在token鉴权。而其他路由则存在TokenVerify鉴权。在该方法中,仅存在key作为鉴权。
而我们用拿到的管理员的token去调用/user/info,可以得到管理员的Secret_key。拿到这个key就可以进行文件上传,而无需token。
在Api.php中的upload方法,上传文件就会调用create方法。
如果是本地上传,又会调用到location_upload($file)方法。
跟进到app\services\UploadCLass.php。
查看它的本地文件上传方法location_upload($file),
可以看见使用了getname方法,我们进而跟进一下getname方法,查看文件名的处理逻辑。
function location_upload($file) { // 获取网站协议 $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://"; $path = './' . $this->getPath; if (!file_exists($path)) { mkdir($path, 0777, true); } $newName = $this->getName($file['name']); // 本地上传 if (move_uploaded_file($file["tmp_name"], $path . $newName)) { $url = $protocol . $_SERVER['HTTP_HOST'] . '/' . $this->getPath . $newName; return array( 'path' => $this->getPath . $newName, 'name' => $newName, 'url' => $url, 'state' => 1, ); } else { return array( 'msg' => '上传失败', 'state' => 0, ); } }
跟进到app\services\UploadCLass.php中的getname()方法。
可以发现该方法没有对 $format(文件后缀)做任何黑白名单校验。它盲目地信任用户上传时的原始文件名。导致了用户可以直接修改后缀为php进行上传。
构造文件上传包,上传webshell。修改文件后缀即可。
哥斯拉可连接:
五、总结
本次审计分析了两个漏洞,一个为jwt密钥硬编码导致任意用户登录,一个为任意文件上传导致的getshell。而我们在代码审计的时候,可以从架构分析入手,重点关注认证授权、文件操作、数据验证等高风险模块,能够有效发现潜在安全隐患。若有纰漏,欢迎讨论。
安全知识分享,欢迎师傅们关注!
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:奕星安全 Yi1StaR Yi1StaR《代审0day-开源图库任意用户登录+getshell》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论