CVE-2019-17059 : Sophos Cyberoam의 Preauth-RCE에 대한 설명

우리는 TheBestVPN의 내부 및 외부 보안 연구원들과 열심히 노력하여 Cyberoam, Fortigate 및 Cisco VPN과 같은 SSL VPN 및 방화벽에서 원격으로 악용 될 수있는 심각한 허점을 발견했습니다. 이 기사는 CyberoamOS로 알려진 Cyberoam SSL VPN에 영향을 미치는 패치 된 중요 취약점에 대한 기술 정보입니다..


CVE-2019-17059라고하는이 Cyberoam 익스플로잇은 공격자가 사용자 이름이나 암호를 제공하지 않고도 Cyberoam 장치에 액세스 할 수있는 중요한 취약점입니다. 또한 부여 된 액세스 권한은 최상위 수준 (루트)으로, 공격자에게 Cyberoam 장치에 대한 무제한 권한을 부여합니다..

대부분의 네트워크 환경에서 Cyberoam 장치는 방화벽 및 SSL VPN 게이트웨이로 사용됩니다. 이는 잠재적 인 공격자에게 네트워크에서 강력한 발판을 제공합니다. 네트워크 내부의 호스트를보다 쉽게 ​​공격 할 수 있으며 Cyberoam 장치는 일반적으로 대부분의 환경에서 신뢰할 수 있기 때문에 공격자에게 추가 우위를 제공합니다.

Shodan (인터넷에 연결된 장치의 검색 엔진)에 따르면 전 세계에 96,000 개가 넘는 인터넷 연결 Cyberoam 장치가 있습니다. 이러한 장치의 대부분은 기업, 대학 및 세계적으로 유명한 은행에 설치됩니다. 이로 인해 이러한 환경에 큰 영향을 미치는 공격이 발생합니다..

Sophos 보안 팀과의 협력은 패치를 처음보고 한 후 며칠 만에 패치를 승인하고 롤아웃함으로써 신속하게 행동했기 때문에 큰 기쁨이었습니다. 그들에게 인기! (pun-intended!)

Cyberoam 감지

그리고 이러한 엔티티의 대부분은 공격자에게 매력적인 대상이므로 버그를 더욱 중요하게 만듭니다..

CyberoamOS 원격 인증되지 않은 루트 명령 실행

CyberoamOS는 Cyberoam 장치를위한 수정 된 Linux 기반 운영 체제입니다. 이 OS에는 웹 기반 구성 인터페이스와 SSLVPN 포털이 있습니다..

웹 인터페이스는 두 가지 주요 부분으로 나뉩니다.

  • 자바로 작성된 프론트 엔드
  • C와 Perl의 조합을 사용하는 백엔드

우리는 프론트 엔드 또는 백엔드 코드의 내부를 깊이 파고 들지 않을 것입니다. 주로 시간을 절약하고 공개 된 정보의 양을 제한하기 위해서입니다. 그러나 우리는 버그가 어떻게 발생하는지 간략하게 논의 할 것입니다.

구성 및 SSLVPN 인터페이스에는 기본 조작을 처리하는 서블릿이 있습니다. 이러한 작업은“mode”라는 매개 변수를 사용하여 정의됩니다..

이들 중 대부분은 인증됩니다. 그러나 로그인없이 인증없이 액세스 할 수있는 몇 가지 작업이 있습니다.

우리가 발견 한 버그는 이메일 안티 바이러스 / 스팸 방지 모듈에 있습니다. 이 엔드 포인트 (모듈, op)의 요청 모드는 458입니다..

한 가지 주목할 점은 opcode가 Cyberoam 데이터베이스 (내부 데이터베이스 Postgres)에서 해당 이름으로 매핑된다는 것입니다. 458을 조회하면이 opcode의 이름이 무엇인지 알 수 있습니다..

다음은 이름 opcode 458을 표시하는 데이터베이스 초기화 SQL 스크립트의 행입니다.

tblcrevent에 삽입 (opcode, 설명, 모드, 요청 유형)
값 ( 'RELEASEQUARANTINEMAILFROMMAIL', 'RELEASE QUARANTINE MAIL FROM MAIL', '458', 2);

opcode 기능은 / _conf / csc / cscconf / 디렉토리에 저장됩니다. 취약한 기능의 전체 코드를 공개하지는 않지만 버그 발생 위치와 방법을 보여주는 몇 가지 스 니펫을 제공합니다..

opcode 458을 처리하는 Java 프론트 엔드 코드 :

if ((jsonObject.getString ("hdnSender"). 같음 ("") ||
validateEmail (jsonObject.getString ("hdnSender"))) &&
validateEmail (jsonObject.getString ("받는 사람")) &&
isSafeFilePath (jsonObject.getString ("hdnFilePath")) && b) {
httpServletResponse.setContentType ("텍스트 / html");
CyberoamLogger.debug ("안티 바이러스 / AntiSpam", "CSC 상수 값 " +
CSCConstants.isCCC);

위에서 볼 수 있듯이 몇 가지 매개 변수가 유효성을 검사합니다. 유효한 값이면 다음이 발생합니다.

최종 EventBean eventByMode = EventBean.getEventByMode (363);
... 편집 됨.
final int sendWizardEvent = cscClient.sendWizardEvent (eventByMode, hashMap, sqlReader);

위에서 볼 수 있듯이 백엔드로 전송 될 새로운 이벤트 코드 (363)가 있습니다. 우리가 발견 한 버그는 백엔드에서이를 처리하는 코드에 있습니다..

opcode의 이름은 sendmail이며이 버그의 악용을 피하기 위해 다음 코드에서 대부분의 코드를 수정합니다..

send_mail의 opcode 핸들러.

...수정 됨 ...

$ param = $ 요청->{해제};
매개 변수 = DLOPEN (base64_decode, param)
로그 앱 로그 " 값 디코딩 :: $ param \ n"
% requestData = split (/ [&=] /, $ param);
$ mailServerHost = $ requestData {hdnDestDomain};
$ mailFrom = $ requestData {hdnSender};
$ mailTo = $ requestData {hdnRecipient};
$ file = $ QUARANTINE_PATH."/".$ requestData {hdnFilePath};

$ mailfile = $ requestData {hdnFilePath};
$ validate_email ="그릇된";
내 $ email_regex = '^ ([\.]? [_ \-\! \ # \ {\} \ $ \ % \ ^ \&\ * \ + \ = \ | \? \ '\\\\\\ / a-zA-Z0-9]) * @ ([a-zA-Z0-9] ([-]? [a-zA- Z0-9] +) * \.) + ([a-zA-Z0-9] {0,6}) $ ';
if ($ requestData {hdnRecipient} = ~ / $ email_regex / && ((정의 $ requestData {hdnSender} && $ requestData {hdnSender} eq '') || $ requestData {hdnSender} = ~ / $ email_regex /) && index ($ requestData {hdnFilePath}, '.. /') == -1) {
$ validate_email ="진실";
}
.... 편집 됨....

위에서 볼 수 있듯이 의사 펄 (Pseudo-Perl) 코드는 백엔드가 프런트 엔드 ($ requestData)로부터 입력을받는 방법과 보내는 파라미터 중 일부를 확인하는 방법을 보여줍니다..

확인 후 매개 변수가 유효하면 다음 코드가 실행됩니다.

% mailreq = ("mailaction"=>"$ MAIL_FORWARD","제목"=>"$ strSubject","이메일로"=>"$ mailTo","첨부 파일"=>"$ 파일","smtpserverhost"=>"$ mailServerHost","주소"=>"$ mailFrom");

out = OPCODE mail_sender json % mailreq

위의 코드는 요청 매개 변수를 mailreq 변수로 설정하고 mail_sender 함수 (OPCODE)를 호출합니다. 이 opcode가 어떻게 실행되고 RCE가 정확히 어디에서 발생하는지 볼 것입니다 :

#mailaction 0 = mail_with_var, 1 = mail_forward, 2 = mail_attachment
$ mailaction = $ 요청->{mailaction};
$ subject = $ 요청->{제목};
$ mailbody = '';
$ attachmentfile = $ 요청->{첨부 파일};
$ toEmail = $ 요청->{이메일로};

# 메일 본문
만약("정의 된 $ 요청->{mailbody} && ''NE $ 요청->{mailbody}") {
$ mailbody = $ 요청->{mailbody};
}
#SMTP 서버 호스트
만약("정의 된 $ 요청->{smtpserverhost} && ''NE $ 요청->{smtpserverhost}") {
$ smtpserverhost = $ 요청->{smtpserverhost};
}그밖에{
결과 = QUERY "servicekey = 'MailServer'인 tblclientservices에서 servicevalue를 선택하십시오."
만약("정의 된 $ result->{산출}->{servicevalue} [0] && ''NE $ 결과->{산출}->{servicevalue} [0]") {
$ smtpserverhost = $ 결과->{산출}->{servicevalue} [0];
}그밖에{
$ smtpserverhost ="127.0.0.1";
}
}

#SMTP 서버 포트
만약("정의 된 $ 요청->{smtpserverport} && ''NE $ 요청->{smtpserverport}") {
$ smtpserverport = $ 요청->{smtpserverport};
}그밖에{
결과 = QUERY "servicekey = 'MailServerPort'인 tblclientservices에서 servicevalue를 선택하십시오."
만약("정의 된 $ result->{산출}->{servicevalue} [0] && ''NE $ 결과->{산출}->{servicevalue} [0]") {
$ smtpserverport = $ 결과->{산출}->{servicevalue} [0];
}그밖에{
$ smtpserverport ="25";
}
}

#SMTP 인증 플래그
$ smtpauthflag ="0";
만약("정의 된 $ 요청->{smtpauthflag} && ''NE $ 요청->{smtpauthflag}") {
$ smtpauthflag = $ 요청->{smtpauthflag};
}그밖에{
결과 = QUERY "servicekey = 'SMTPAuthenticationFlag'인 tblclientservices에서 servicevalue를 선택하십시오."
만약("정의 된 $ result->{산출}->{servicevalue} [0] && ''NE $ 결과->{산출}->{servicevalue} [0]") {
$ smtpauthflag = $ 결과->{산출}->{servicevalue} [0];
}
}

만약("$ smtpauthflag == 1") {
만약("정의 된 $ 요청->{mailusername} && ''NE $ 요청->{mailusername}") {

$ mailusername = $ 요청->{mailusername};
$ mailpassword = $ 요청->{mailpassword};

}그밖에{
결과 = QUERY "servicekey = 'MailServerUsername'인 tblclientservices에서 servicevalue를 선택하십시오."
$ mailusername = $ result->{산출}->{servicevalue} [0];
결과 = QUERY "servicekey = 'MailServerPassword'인 tblclientservices에서 servicevalue를 선택하십시오."
$ mailpassword = $ result->{산출}->{servicevalue} [0];
}
}그밖에{

$ mailusername = "";
$ mailpassword = "";

}
만약("정의 된 $ 요청->{fromaddress} && ''NE $ 요청->{fromaddress}") {
$ fromaddress = $ 요청->{fromaddress};
}그밖에{
결과 = QUERY "servicekey = 'FromAddress'인 tblclientservices에서 servicevalue를 선택하십시오."
$ from 주소 = $ 결과->{산출}->{servicevalue} [0];
}

# 보안 모드
만약("정의 된 $ 요청->{smtpsecurity} && ''NE $ 요청->{smtpsecurity}") {
$ smtpsecurity = $ 요청->{smtpsecurity};
}그밖에{
결과 = QUERY "servicekey = 'smtpsecurity'인 tblclientservices에서 servicevalue를 선택하십시오."
$ smtpsecurity = $ result->{산출}->{servicevalue} [0];
}

$ smtpsecuritymode = 0;
만약("$ smtpsecurity eq 'STARTTLS'") {
$ smtpsecuritymode = 1;
} 다른 경우 ("$ smtpsecurity eq 'SSL / TLS'") {
$ smtpsecuritymode = 2;
}

#SMTP 인증서

$ smtpcertificate = '';
$ certpassword = '';

만약("$ smtpsecuritymode! = 0") {
만약("정의 된 $ 요청->{smtpcertificate} && ''NE $ 요청->{smtpcertificate}") {
결과 = QUERY "tblvpncertificate에서 certname, password를 선택하십시오. 여기서 certid = $ request->{smtpcertificate}"
}그밖에{
결과 = QUERY "tblvpncertificate에서 certname, password를 선택하십시오. 여기서 certid = (select servicevalue :: int는 tblclientservices에서 servicekey = 'smtpcertificate')"
}

$ smtp 인증서 = $ result->{산출}->{certname} [0];
$ certpassword = $ 결과->{산출}->{암호} [0];

}

# 이름으로 주소에서
만약("정의 된 $ 요청->{fromaddresswithname} && ''NE $ 요청->{fromaddresswithname}") {
$ fromaddresswithname = $ 요청->{fromaddresswithname};
}그밖에{
$ fromaddresswithname = $ OEMNAME . " <" . $ 주소 . ">";
}

위의 코드는 다른 opcode가 시작될 때와 동일한 기능을 수행합니다. 변수를 초기화합니다 (일부 지정하지 않은 경우 당사 또는 장치에서).

변수가 할당되면 다음 코드 블록이 실행됩니다..

아웃 = EXECSH "/ bin / cschelper mail_send '$ fromaddress' '$ fromaddresswithname' '$ toEmail' '$ toEmail' '$ subject' '$ mailbody' '$ smtpserverhost' '$ smtpserverport' '$ mailusername' '$ mailpassword' '$ mailaction' ' $ smtpsecuritymode ''$ smtpcertificate ''$ certpassword ''1 ''$ attachmentfile '"

그리고 거기에는 명령 실행이 있습니다. 여기서 호출은 EXECSH이며 / bin / sh -c“인수”를 호출합니다. 제어하는 값을 사용하여 실행하면 인증 없이도 원격 명령 실행을 쉽게 달성 할 수 있습니다..

몇 달 안에 전체 보고서와 개념 증명을 적절한 개요로 발표 할 것입니다..

최신 정보: 이 연구는 TechCrunch에서 처음 다루었습니다. 자세한 내용은 여기를 참조하십시오..

Brayan Jackson
Brayan Jackson Administrator
Sorry! The Author has not filled his profile.
follow me