CVE-2019-17059: Preauth-RCE ใน Cyberoam อธิบาย

เราได้ทำงานอย่างหนักกับนักวิจัยด้านความปลอดภัยภายในและภายนอกที่นี่ที่ TheBestVPN เพื่อค้นหาช่องโหว่ที่หาประโยชน์ได้จากระยะไกลใน SSL VPN และไฟร์วอลล์เช่น Cyberoam, Fortigate และ Cisco VPN บทความนี้เป็นบทความเชิงเทคนิคเกี่ยวกับช่องโหว่ที่สำคัญที่มีการปะแก้ซึ่งมีผลต่อ Cyberoam SSL VPN หรือที่เรียกว่า CyberoamOS.


ช่องโหว่ของ Cyberoam นี้ถูกขนานนามว่า CVE-2019-17059 เป็นช่องโหว่ที่สำคัญที่ทำให้ผู้โจมตีเข้าถึงอุปกรณ์ Cyberoam ของคุณโดยไม่ต้องระบุชื่อผู้ใช้หรือรหัสผ่านใด ๆ ยิ่งไปกว่านั้นการเข้าถึงที่ได้รับคือระดับสูงสุด (รูท) ซึ่งให้สิทธิการโจมตีแบบไม่ จำกัด บนอุปกรณ์ Cyberoam ของคุณ.

ในสภาพแวดล้อมเครือข่ายส่วนใหญ่อุปกรณ์ Cyberoam จะใช้เป็นไฟร์วอลล์และเกตเวย์ VPN VPN สิ่งนี้จะช่วยให้ผู้โจมตีที่มีศักยภาพตั้งหลักในเครือข่าย ทำให้ง่ายต่อการโจมตีโฮสต์ภายในเครือข่ายและเนื่องจากอุปกรณ์ Cyberoam มักเชื่อถือได้ในสภาพแวดล้อมส่วนใหญ่สิ่งนี้จะช่วยให้ผู้โจมตีได้เปรียบเป็นพิเศษ.

จากข้อมูลของ Shodan (เสิร์ชเอ็นจิ้นสำหรับอุปกรณ์ที่เชื่อมต่ออินเทอร์เน็ต) มีอุปกรณ์ Cyberoam ที่หันหน้าเข้าหาอินเทอร์เน็ตมากกว่า 96,000 เครื่องจากทั่วทุกมุมโลก อุปกรณ์เหล่านี้ส่วนใหญ่ติดตั้งในองค์กรมหาวิทยาลัยและบางแห่งในธนาคารที่มีชื่อเสียงระดับโลก สิ่งนี้นำไปสู่การโจมตีที่มีผลกระทบอย่างมากต่อสภาพแวดล้อมเหล่านี้.

การทำงานกับทีมรักษาความปลอดภัยของ Sophos นั้นเป็นเรื่องที่น่ายินดีอย่างยิ่งเนื่องจากพวกเขาดำเนินการอย่างรวดเร็วโดยรับทราบและนำเสนอแพทช์เพียงไม่กี่วันหลังจากที่เรารายงานเบื้องต้น ขอชื่นชมพวกเขา! (เล่นสำนวนตั้งใจ!)

ตรวจจับ Cyberoam

และเนื่องจากเอนทิตีเหล่านี้ส่วนใหญ่เป็นเป้าหมายที่น่าดึงดูดสำหรับผู้โจมตีจึงทำให้บั๊กทั้งหมดนั้นสำคัญยิ่งกว่า.

การเรียกใช้งานคำสั่งรูทที่ไม่ได้รับการพิสูจน์ตัวตนทางไกลจาก CyberoamOS

CyberoamOS เป็นระบบปฏิบัติการบน Linux ที่ได้รับการดัดแปลงสำหรับอุปกรณ์ Cyberoam ระบบปฏิบัติการนี้มีอินเตอร์เฟสการกำหนดค่าบนเว็บและพอร์ทัล SSLVPN.

เว็บอินเตอร์เฟสแบ่งออกเป็นสองส่วนหลัก:

  • ส่วนหน้าเขียนใน Java
  • แบ็กเอนด์ที่ใช้การรวมกันของ C และ Perl

เราจะไม่ดำดิ่งลึกเข้าไปใน internals ของรหัส front-end หรือ back-end ส่วนใหญ่เพื่อประหยัดเวลาและ จำกัด ปริมาณของข้อมูลที่เปิดเผย แต่เราจะพูดคุยสั้น ๆ ว่าข้อผิดพลาดเกิดขึ้นได้อย่างไร.

ทั้งคอนฟิกูเรชันและอินเตอร์เฟส SSLVPN มีเซิร์ฟเล็ตที่จัดการการดำเนินการหลัก การดำเนินการเหล่านี้ถูกกำหนดโดยใช้พารามิเตอร์ชื่อ“ โหมด”.

ส่วนใหญ่เหล่านี้มีการรับรองความถูกต้อง แต่มีไม่กี่ตัวเลือกที่เราสามารถเข้าถึงได้โดยไม่ต้องมีการตรวจสอบสิทธิ์ (เช่นเข้าสู่ระบบ).

ข้อบกพร่องที่เราพบนั้นอยู่ในโมดูลป้องกันไวรัส / แอนตี้สแปมของอีเมล โหมดการร้องขอสำหรับจุดปลายนี้ (โมดูล, op) คือ 458.

สิ่งหนึ่งที่ควรทราบคือ opcodes ถูกแมปกับชื่อของพวกเขาในฐานข้อมูล Cyberoam (Postgres ฐานข้อมูลภายใน) โดยการค้นหา 458 เราสามารถค้นหาว่าชื่อของรหัสนี้คืออะไร.

นี่คือบรรทัดจากการเริ่มต้นฐานข้อมูลสคริปต์ SQL ที่แสดงชื่อ opcode 458:

แทรกลงใน tblcrevent (opcode, คำอธิบาย, โหมด, requesttype)
ค่า ('RELEASEQUARANTINEMAILFROMMAIL', 'RELEASE QUARANTINE MAIL จาก MAIL', '458', 2);

ฟังก์ชั่น opcode จะถูกเก็บไว้ในไดเรกทอรี / _conf / csc / cscconf / เราจะไม่เปิดเผยรหัสทั้งหมดของฟังก์ชันที่มีช่องโหว่ แต่เราจะให้ตัวอย่างบางส่วนที่แสดงตำแหน่งและวิธีที่ข้อบกพร่องเกิดขึ้น.

รหัสจากส่วนหน้า Java ที่จัดการ opcode 458:

ถ้า ((jsonObject.getString ("hdnSender") .equals ("") ||
validateEmail (jsonObject.getString ("hdnSender"))) &&
validateEmail (jsonObject.getString ("hdnRecipient")) &&
isSafeFilePath (jsonObject.getString ("hdnFilePath")) && b) {
httpServletResponse.setContentType ("text / html");
CyberoamLogger.debug ("โปรแกรมป้องกันไวรัส / AntiSpam", "CSC ค่าคงที่ " +
CSCConstants.isCCC);

ดังที่คุณเห็นด้านบนมีการตรวจสอบความถูกต้องของพารามิเตอร์บางอย่าง หากเป็นค่าที่ถูกต้องข้อมูลต่อไปนี้จะเกิดขึ้น:

สุดท้าย EventBean eventByMode = EventBean.getEventByMode (363);
... redacted
สุดท้าย int sendWizardEvent = cscClient.sendWizardEvent (eventByMode, hashMap, sqlReader);

ดังที่เราเห็นด้านบนเรามีรหัสเหตุการณ์ใหม่ (363) ที่จะถูกส่งไปที่แบ็กเอนด์ ข้อผิดพลาดที่เราค้นพบอยู่ในรหัสที่จัดการสิ่งนี้ในแบ็กเอนด์.

opcode ชื่อ sendmail และเพื่อหลีกเลี่ยงการใช้ประโยชน์จากข้อบกพร่องนี้เราจะ redacting รหัสส่วนใหญ่จากรหัสต่อไปนี้.

ตัวจัดการ opcode สำหรับ send_mail.

...redacted ...

$ param = คำขอ $->{ปล่อย};
param = DLOPEN (base64_decode, param)
log applog " ค่าถอดรหัส :: $ param \ n"
% requestData = แยก (/ [&=] /, $ param);
$ mailServerHost = $ requestData {hdnDestDomain};
$ mailFrom = $ requestData {hdnSender};
$ mailTo = $ requestData {hdnRecipient};
$ file = $ QUARANTINE_PATH."/".$ requestData {hdnFilePath};

$ = $ mailfile requestData {hdnFilePath};
$ validate_email ="เท็จ";
$ email_regex = my ^ ([\.]? [_ \ - \! \ # \ {\} \ $ \% \ ^ \&\ * \ + \ = \ |? \ \ '\\\\\\ / 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 /) && ดัชนี ($ requestData {hdnFilePath}, '.. /') == -1) {
$ validate_email ="จริง";
}
.... redacted....

ดังที่เราเห็นด้านบนรหัสหลอก - Perl แสดงให้เราเห็นว่าแบ็กเอนด์ได้รับข้อมูลจากส่วนหน้า ($ requestData) อย่างไรและมันพยายามตรวจสอบพารามิเตอร์บางอย่างที่เราส่ง.

หลังจากการตรวจสอบหากพารามิเตอร์ของเราถูกต้องรหัสต่อไปนี้จะถูกดำเนินการ:

% mailreq = ("mailaction"=>"$ MAIL_FORWARD","เรื่อง"=>"$ strSubject","toEmail"=>"$ mailto","attachmentfile"=>"$ ไฟล์","smtpserverhost"=>"$ mailServerHost","fromaddress"=>"$ mailFrom");

ออก = OPCODE mail_sender json% mailreq

รหัสด้านบนตั้งค่าพารามิเตอร์คำขอของเราเป็นตัวแปร mailreq และเรียกใช้ฟังก์ชัน mail_sender (OPCODE) เราจะดูว่า opcode นี้ทำงานอย่างไรและ RCE นั้นเกิดขึ้นได้อย่างไร:

#mailaction 0 = mail_with_var, 1 = mail_forward, 2 = mail_attachment
$ = $ mailaction คำขอ->{mailaction};
$ = $ เรื่องคำขอ->{เรื่อง};
$ mailbody = '';
$ = $ attachmentfile คำขอ->{attachmentfile};
$ = $ toEmail คำขอ->{toEmail};

#mail เนื้อหา
ถ้า("คำขอ $ ที่กำหนดไว้->{mailbody} && '' ต้องการ $ ne->{mailbody}") {
$ = $ mailbody คำขอ->{mailbody};
}
โฮสต์เซิร์ฟเวอร์ #SMTP
ถ้า("คำขอ $ ที่กำหนดไว้->{smtpserverhost} && '' ต้องการ $ ne->{smtpserverhost}") {
$ = $ smtpserverhost คำขอ->{smtpserverhost};
}อื่น{
ผล = QUERY "เลือก servicevalue จาก tblclientservices โดยที่ servicekey = 'MailServer'"
ถ้า("กำหนดผลลัพธ์ $->{} เอาท์พุท->{servicevalue} [0] && '' ne $ result->{} เอาท์พุท->{servicevalue} [0]") {
$ = $ smtpserverhost ผล->{} เอาท์พุท->{servicevalue} [0];
}อื่น{
$ smtpserverhost ="127.0.0.1";
}
}

พอร์ตเซิร์ฟเวอร์ #SMTP
ถ้า("คำขอ $ ที่กำหนดไว้->{smtpserverport} && '' ต้องการ $ ne->{smtpserverport}") {
$ = $ smtpserverport คำขอ->{smtpserverport};
}อื่น{
ผล = QUERY "เลือก servicevalue จาก tblclientservices โดยที่ servicekey = 'MailServerPort'"
ถ้า("กำหนดผลลัพธ์ $->{} เอาท์พุท->{servicevalue} [0] && '' ne $ result->{} เอาท์พุท->{servicevalue} [0]") {
$ = $ smtpserverport ผล->{} เอาท์พุท->{servicevalue} [0];
}อื่น{
$ smtpserverport ="25";
}
}

ธง #SMTP รับรองความถูกต้อง
$ smtpauthflag ="0";
ถ้า("คำขอ $ ที่กำหนดไว้->{smtpauthflag} && '' ต้องการ $ ne->{smtpauthflag}") {
$ = $ smtpauthflag คำขอ->{smtpauthflag};
}อื่น{
ผล = QUERY "เลือก servicevalue จาก tblclientservices โดยที่ servicekey = 'SMTPAuthenticationFlag'"
ถ้า("กำหนดผลลัพธ์ $->{} เอาท์พุท->{servicevalue} [0] && '' ne $ result->{} เอาท์พุท->{servicevalue} [0]") {
$ = $ smtpauthflag ผล->{} เอาท์พุท->{servicevalue} [0];
}
}

ถ้า("$ smtpauthflag == 1") {
ถ้า("คำขอ $ ที่กำหนดไว้->{mailusername} && '' ต้องการ $ ne->{mailusername}") {

$ = $ mailusername คำขอ->{mailusername};
$ = $ mailpassword คำขอ->{mailpassword};

}อื่น{
ผล = QUERY "เลือก servicevalue จาก tblclientservices โดยที่ servicekey = 'MailServerUsername'"
$ mailusername = ผลลัพธ์ $->{} เอาท์พุท->{servicevalue} [0];
ผล = QUERY "เลือก servicevalue จาก tblclientservices โดยที่ servicekey = 'MailServerPassword'"
$ mailpassword = ผลลัพธ์ $->{} เอาท์พุท->{servicevalue} [0];
}
}อื่น{

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

}
ถ้า("คำขอ $ ที่กำหนดไว้->{fromaddress} && '' ต้องการ $ ne->{fromaddress}") {
$ = $ fromaddress คำขอ->{fromaddress};
}อื่น{
ผล = QUERY "เลือก servicevalue จาก tblclientservices โดยที่ servicekey = 'FromAddress'"
$ fromaddress = ผลลัพธ์ $->{} เอาท์พุท->{servicevalue} [0];
}

โหมดความปลอดภัย #
ถ้า("คำขอ $ ที่กำหนดไว้->{smtpsecurity} && '' ต้องการ $ ne->{smtpsecurity}") {
$ = $ smtpsecurity คำขอ->{smtpsecurity};
}อื่น{
ผล = QUERY "เลือก servicevalue จาก tblclientservices โดยที่ servicekey = 'smtpsecurity'"
$ smtpsecurity = ผลลัพธ์ $->{} เอาท์พุท->{servicevalue} [0];
}

$ smtpsecuritymode = 0;
ถ้า("$ smtpsecurity eq 'STARTTLS'") {
$ smtpsecuritymode = 1;
} ถ้าอื่น ๆ ("$ smtpsecurity eq 'SSL / TLS'") {
$ smtpsecuritymode = 2;
}

#SMTP ใบรับรอง

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

ถ้า("$ smtpsecuritymode! = 0") {
ถ้า("คำขอ $ ที่กำหนดไว้->{smtpcertificate} && '' ต้องการ $ ne->{smtpcertificate}") {
ผล = QUERY "เลือก certname รหัสผ่านจาก tblvpncertificate โดยที่ certid = $ ร้องขอ->{smtpcertificate}"
}อื่น{
ผล = QUERY "เลือก certname, รหัสผ่านจาก tblvpncertificate โดยที่ certid = (เลือก servicevalue :: int จาก tblclientservices โดยที่ servicekey = 'smtpcertificate')"
}

$ smtpcertificate = $ ผลลัพธ์->{} เอาท์พุท->{certname} [0];
$ = $ certpassword ผล->{} เอาท์พุท->{} รหัสผ่าน [0];

}

# จากที่อยู่พร้อมชื่อ
ถ้า("คำขอ $ ที่กำหนดไว้->{fromaddresswithname} && '' ต้องการ $ ne->{fromaddresswithname}") {
$ = $ fromaddresswithname คำขอ->{fromaddresswithname};
}อื่น{
$ fromaddresswithname = $ OEMNAME . " <" . $ fromaddress . ">";
}

รหัสด้านบนทำในสิ่งเดียวกันกับที่ opcode อื่นทำเมื่อเริ่มทำงาน มันเริ่มต้นตัวแปร (บางส่วนจากเราหรือจากอุปกรณ์หากไม่ได้ระบุไว้).

หลังจากกำหนดตัวแปรแล้วโค้ดบล็อกต่อไปนี้จะถูกดำเนินการ.

ออก = EXECSH "/ bin / cschelper mail_send '$ fromaddress' '$ fromaddresswithname' '$ toEmail' '$ toEmail' '$ subject' '$ mailbody' '$ smtpserverhost' '$ smtpserverport' '$ mailpassword' '$ mailpassword' '$ mailaction' ' $ smtpsecuritymode '' $ smtpcertificate '' $ certpassword '' 1 '' $ ไฟล์แนบ '"

และนั่นก็คือการประมวลผลคำสั่ง ตอนนี้การโทรที่นี่คือ EXECSH ซึ่งการเรียก / bin / sh -c“ ARGUMENTS” ด้วยการดำเนินการที่เกิดขึ้นโดยใช้ค่าที่เราควบคุมเราสามารถบรรลุการดำเนินการคำสั่งระยะไกลได้อย่างง่ายดาย.

เราจะปล่อยรายงานฉบับเต็มและหลักฐานการแนวคิดที่มีโครงร่างที่เหมาะสมในไม่กี่เดือน.

ปรับปรุง: งานวิจัยนี้ได้รับการกล่าวถึงเป็นอันดับแรกใน TechCrunch อ่านเพิ่มเติมได้ที่นี่.

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