phpList 3.5.0弱类型漏洞复现及渗透测试实战经验分享

在一次渗透测试过程中,甲方的维护工作做得比较到位。所以,并没有发现什么漏洞。而在即将结束的时候,我依然没有取得大的突破。

phpList 3.5.0弱类型漏洞复现及渗透测试实战经验分享

我进行了一番仔细的信息搜集。接着发现系统里有 phpList。这让我眼前一亮,因为我记得之前看到过 phplist 的相关漏洞。

我随即开始查找 phplist 的历史漏洞。经过一番操作后,最终明确 phplist 的版本是 3.5.0,其存在的漏洞是弱类型漏洞。利用这个弱类型漏洞成功登录进去,之后事情就进展得很顺利。

因为是在客户机器上进行的渗透,所以没有截图之类的东西……接下来我在本机上搭建了一个环境,要对 phplist3.5.0 的弱类型漏洞进行复现。

复现过程

phpList 3.5.0 版本存在安全漏洞,其编号为 CVE-2020-5847。此漏洞是因为程序在处理开头为 0e 且之后全部为数字字符的哈希值时出现错误。远程攻击者能够借助该漏洞绕过管理员账户的身份验证。

先下载 phpList 3.5.0-RC1,接着进行解压操作,之后找到该目录。

将这个目录拷贝到网站目录下,并重命名。

对 config/config.php 配置文件进行修改,明确需连接的数据库以及账户密码的相关设置。

同时建立phplistdb数据库。

访问;

接下来初始化安装,设置管理员的账号密码:

这里的密码需要设置为经过 sha256 处理后开头为 0e 的字符串,例如 TyNOQHUS。

我们再次访问后台。我们以密码 34250003024812 进行登录。该密码 sha256 后是以 0e 开头的。

“TyNOQHUS——hash :

该字符串为“0e66298694359207596086558843543959518835691168370379069085300385”。

“34250003024812——hash:

“0e46289032038065916139621039085883773413820991920706299695051332” 这个字符串具有特定的编码或标识意义,可能在特定的系统、数据或情境中被使用和识别。它由一系列数字组成,每个数字都有其特定的位置和价值。这些数字的组合形成了一个独特的代码,用于区分或标识特定的对象、事件或信息。

登录成功,漏洞验证成功:

下面还是具体分析一下漏洞处的代码。

验证管理员登录的PHP文件为:

“phpListAdminAuthentication.php”这个文件。

登录时,会传入密码。经过 if 判断后,能够看到$encryptedPass(密码 sha256 后的的值)是通过==来判断其与数据库中的值是否相同,也就是$encryptedPass == $passwordDB。

PHP 存在弱类型比较的情况,这会导致 0exxxxx 与 0eyyyyy 相等(因为会将每一个以“0e”开头的哈希值都解读为 0),其具体代码呈现如下:

什么是PHP弱类型

首先先说一下,什么是强类型和弱类型?

语言通常分为强类型和弱类型这两种。强类型指的是强制规定数据类型的语言。也就是说,一个变量一旦被定义为某个类型,在不进行强制类型转换的情况下,这个变量就始终是这个类型。

在使用变量之前,需要声明变量的类型和名称。并且,在未经强制转换的情况下,不允许两种不同类型的变量进行相互操作。然而,弱类型则能够随意地转换变量的类型。

PHP是最受欢迎的开源脚本语言,在 Web 开发领域的应用越来越多。并且 PHP 属于弱类型语言,在定义变量时无需声明其类型。

弱类型给程序员书写代码带来了很大便利。然而在安全领域,特性意味着漏洞。这些特性在代码中常常是漏洞最容易出现的地方。

PHP 官方给出了类型比较表。该表格显示了 PHP 类型以及比较运算符在松散比较和严格比较时所起的作用。

松散比较:

严格比较:

总结一下PHP弱类型产生漏洞的原理:

通过以上方式能够产生诸如密码重置这样的情况,还能绕过管理员账户的身份验证,以及出现注入、cookie 伪造等弱类型漏洞。并且一些 CTF 试题会借助这些弱类型来设置诸如 MD5 碰撞、十六进制转换等问题。

下面通过一些漏洞实例来进行说明。

CVE-2014-0166 是关于 WordPress 的 Cookie 伪造漏洞。

在wordpress-3.8.2的补丁中有以下代码:

在 wordpress - 3.8.1 中,其对应的 PHP 代码是如下这样的:

通过对比我们自然把全部的关注点放到!=与!==上来。

再看php manual中给出的例子:


var_dump(0 等于 "a");  // 0 等于 0 -> true ,这里比较的是值的相等性,0 与字符串 "a" 不相等,所以结果为 false ,而不是 true 。var_dump("1" == "1");  // 1 与 1 相等,结果为 truevar_dump("10" 等于 "1e1");  // 10 等于 10 -> truevar_dump(100 等于 "1e2");  // 100 等于 100 -> true?>


字符串在与数字比较前会自动转换为数字,所以0=="a"了。

回到wordpress的验证代码上来。先生成依据用户名($username)、密码($pass_frag)、cookie 有效期 ($expiration)以及 wp-config.php 中的 key($key)这四个信息,算出与之对应的$hash。接着,用从 cookie 中获取到的$hmac 值和算出的$hash 进行比较($hmac 不等于$hash),以此来验证 cookie 的有效性。

cookie的格式是这样的:

wordpress 的哈希值由 url、用户名、过期时间和 HMAC 组成,其形式为 username|expiration|hmac

我们可以控制的变量有两个,分别是$username 和$expiration。$username 是需要固定的。这样一来,我们就能够通过对 cookie 中的$expiration 进行控制,从而改变$hash 的值。之后,再把 cookie 中的$hmac 设置为 0。

只要持续改变$expiration,然后去寻找能够满足$hash=="0"的$hash,这样就成功地伪造了有效的 cookie。

HDwiki sql注入

在/control/list.php中,代码如下:

分析代码后可以得知,从 GET 中获取到了 $doctype,也就是 $doctype = $this->get[2],随后进入了一个 switch 语句。接着我们发现,后面直接把 $doctype 运用到了 SQL 语句中。

使用“focus”表,通过“type=$doctype”条件进行查询,获取到的总数赋值给变量$count,即$count 等于$this->db->fetch_total('focus',"type=$doctype")。

只要这个 switch 语句不会对 $doctype 的值产生影响,那么后面就可以进行注入了。

如果进入 default ,会将 $doctype 改为 1 。

这里犯了一个“弱类型”的错误。当出现一个字符串与一个数字进行比较的情况时,会先把字符串进行强制类型转换,然后再与数字进行比较。

所以当我传入 doctype 为 2xxxx 时,实际上会进入 case 2 而非 default。在跳出 switch 语句后,doctype 的值依然是 2xxxx,并非 2。

所以之后的:

$count=$this->db->fetch_total('focus',"type=$doctype");

$doctype带入SQL语句造成注入。

CTF中的利用

《MD5碰撞》:

如果存在 $_GET['Username'] 并且存在 $_GET['password'] 。      $logined = true;获取的 GET 请求中的 Username 被赋值给了 $Username 。获取到的名为 password 的变量的值为通过 GET 方式传递过来的 password 的值。即通过 GET 请求获取到的 password 被赋值给了变量 $password 。如果 $Username 不是字母字符,那么 $logined 为 false。假如密码不是数字性的,那么登录状态就为 false。如果 md5 处理后的 $Username 不等于 md5 处理后的 $password,那么 $logined 为 false。    if ($logined){    echo "successful";      }else{            echo "login failed!";         }     } ?>

这个题目的意思是,输入一个数字,同时输入一个字符串,只有当这两者的 MD5 值相同时,才能够得到 successful。

在进行比较时,会将其视为科学计数法。因此,无论 0e 后面是什么,0 的多少次方都为 0。

我们只需输入一个数字和字符串,对其进行 MD5 加密后都得到 0e,就能得出答案。

md5 的值为 '240610708' 与 md5 的值为 'QNKCDZO' 是相等的。

成功绕过。

《起名字真难》:

如果函数 noother_says_correct 接收到一个参数 $number,那么它会进行相应的操作或判断,具体的行为取决于该函数内部的逻辑和设计。这个函数可能会根据 $number 的值来执行不同的代码路径,以实现特定的功能或返回特定的结果。{       $one = ord('1');       $nine = ord('9');       for ($i = 0; $i < strlen($number); $i++)       {                $digit = ord($number{$i});如果(数字大于等于 1)并且(数字小于等于 9),那么(数字是一位数);如果(数字大于等于 10)并且(数字小于等于 99),那么(数字是两位数);如果(数字大于等于 100)并且(数字小于等于 999),那么(数字是三位数);如果(数字大于等于 1000)并且(数字小于等于 9999),那么(数字是四位数);如果(数字大于等于 10000)并且(数字小于等于 99999),那么(数字是五位数);如果(数字大于等于 100000)并且(数字小于等于 999999),那么(数字是六位数);如果(数字大于等于 1000000)并且(数字小于等于 9999999),那么(数字是七位数);如果(数字大于等于 10000000)并且(数字小于等于 99999999),那么(数字是八位数);如果(数字大于等于 100000000)并且(数字小于等于 999999999),那么(数字是九位数);如果(数字大于等于 1000000000),那么(数字是十位数及以上)。<= $nine) )               {                       return false;               }       }如果数字等于 '54975581388',则返回 true;否则返回 false。}$flag='*******';如果没有其他的(判断)认为 $_GET['key'] 是正确的。   echo $flag;else   echo 'access denied';?>

题目大致的意思为,需输入一串 key,此 key 不能是数字形式,然而却必须与数字 54975581388 相等,这样才能拿到 flag。

看完题目就知道要求字符串和数字进行比较,所以:

$number == '54975581388';

看到这便联想到了弱类型,与之相匹配的十六进制字符串是 0xccccccccc,而这个字符串对应的数字是 54975581388。

全不是数字,自然就绕过了,得到flag。

结语

看到这里,大家应该都对 PHP 弱类型比较有所了解了。同时,还有许多其他的漏洞实例存在,不过在此就不一一进行列举了。

下面列举一些以0e开头的字符串的md5的hash值:

s214587387a:

0e848240448830537924465865611904 这串字符具有特定的标识意义,它可能代表着某个特定的信息、数据或代码片段等,在特定的情境或系统中有着特定的用途和价值。

s1502113478a:

0e861580163291561247404381396064 这个内容;它具有特定的标识或特征;可能在特定的情境或系统中有其特定的意义或用途。

s1091221200a:

0e940624217856561557816327384675 这串字符本身就具有特定的含义和价值,可能在特定的系统或情境中有着重要的作用。它或许是某个数据的标识,也可能是一段代码的关键部分,或者是一个特定操作的触发因素。无论其具体用途如何,这串字符都承载着一定的信息和意义。

s1665632922a:

0e731198061491163073197128363787 这个内容没有具体的上下文信息,仅从这串字符本身来看,它是一个特定的编码或标识,可能在某个特定的系统、场景或数据集中具有特定的含义。但仅凭借这串字符,无法确切知晓其具体用途和所代表的信息。

s1885207154a:

0e509367213418206700842008763514 是一个特定的编码或标识,它可能在特定的系统或情境中有特定的含义和用途。这个编码可能与数据、文件、事件或其他相关的事物相关联。它可能是用于识别、跟踪或管理特定对象或信息的一种方式。具体的含义和用途需要根据相关的背景和上下文来确定。

s1836677006a:

关于这串字符的相关信息等。

s1665632922a:

0e731198061491163073197128363787;

s878926199a:

0e545993274517709034328855841020 这个内容;它是一个特定的标识或编码等;有着独特的存在形式和意义;在特定的情境或系统中具有特定的作用。

QLTHNDT:

0e405967825401955372549139051580 这串字符具有特定的标识意义,它在特定的情境或系统中有着独特的作用和价值。

QNKCDZO:

0e830400451993494058024219903391 这串字符具有特定的标识意义,可能在特定的系统或情境中被使用,它有着独特的编码和组合形式,以区分其他不同的信息或对象。

EEIZDOI:

0e782601363539291779881938479162 这个内容没有具体的上下文信息,仅从这串字符本身来看,它是一个特定的标识或数据序列,没有更多的含义或解释。它可能在某个特定的系统、程序或情境中具有特定的作用,但仅从这几个字符本身无法确切知晓其具体用途。

TUFEPMC:

0e839407194569345277863905212547 这串字符具有特定的标识意义;它是一个独一无二的编码组合;在特定的系统或情境中被用于区分和标识特定的对象或信息;具有明确的唯一性和特定性。

UTIPEZQ:

0e382098788231234954670291303879 这串字符具有特定的标识意义。它是一个独一无二的编码或标识,用于在特定的系统或情境中进行区分和识别。可能与某个特定的项目、数据或操作相关联,在相关的领域或环境中具有重要的作用。

UYXFLOI:

0e552539585246568817348686838809 这串字符具有特定的标识意义,它可能代表着某个特定的信息、数据或代码等,在特定的系统或情境中有着独特的作用和价值。

IHKFRNS:

0e256160682445802696926137988570 这个内容本身没有具体含义的上下文,仅从这串字符来看,它就是这样一个特定的组合。

240610708:

0e462097431906509019562988736854 这串字符具有特定的标识意义。它可能是某个系统或数据中的唯一编码,用于区分不同的对象或记录。在特定的情境中,它承载着重要的信息和价值。

314282422:

此句仅为一串字符,没有具体的句子内容可供改写呀,你可以给我提供具体的句子呢。

571579406:

0e972379832854295224118025748221 是一个特定的标识或代码,可能在特定的系统或情境中有其特定的含义和用途。它可能与某个数据、事件、操作或对象相关联,具体的意义需要根据相关的背景和上下文来确定。

903251147:

0e174510503823932942361353209384 这个内容没有具体的上下文信息,仅从这串字符本身来看,它只是一个特定的标识或数据序列,可能在某个特定的系统、环境或情境中有其特定的意义和用途。

1110242161:

0e435874558488625891324861198103 这个内容没有具体的上下文,难以准确理解其含义和进行改写。如果能提供更多关于这个内容的背景信息或具体要求,我将能更好地为你提供帮助。

1320830526:

0e912095958985483346995414060832 这个内容。

1586264293:

0e622743671155995737639662718498 这串字符;这串特定的数字组合;具有该特定编码的内容;以 0e622743671155995737639662718498 为标识的事物;包含 0e622743671155995737639662718498 的信息。

感谢阅读,下次再见!

侵权请私聊公众号删文

你可能想看:
« 上一篇
下一篇 »

相关推荐

php实时推送,php实地培训

2026年10月05日

53阅读

php乱码出现问号的原因,php乱码出现问号的原因有哪些

2026年10月05日

45阅读

php一句话木马,一句话木马ctf

2026年10月05日

48阅读

php获取数组长度,php中如何获取数组的长度

2026年10月05日

50阅读

php面试题库及答案,php程序员面试真题库

2026年10月05日

47阅读

phpcontent-type修改,php源码二次修改需要多少钱

2026年10月05日

42阅读