AWDP

CCforum

粗略审计后大概可以知道应该是要先登陆进admin.php,但sql语句全部做了预编译所以没法注入,试了一下通过爆破可以获取到admin.php的账号密码。登陆进去后再看有什么利用点

入口没有什么特别的,可以直接先看到login的逻辑

image-20250318193846731

可以看到把通过验证的用户名写到了session里,接着看reply.php和post.php会发现都有一个敏感词过滤。并且他们调用的$username都是session里的明文用户名

image-20250318194012064

image-20250318194049873

跟进到config.php看这两个函数

image-20250318194140132

可以看到record_banned函数里主要干了三件事

  1. 拿用户名base64的结果创建目录
  2. 判断是否创建成功,如果创建成功就正常写入被过滤的敏感词。创建失败有两个分支,一个是文件夹创建失败,会把用户名明文拿去和日志信息做拼接;一个是banned日志文件创建失败,直接写入一串信息,没有拼接。
  3. 最后将第二点走完的结果用log_action函数写到总日志里(这里又个很容易掉进去的陷阱,就是日志其实是有两份的,一个是总日志access.log,一个是banned日志。我们能作拼接任意写入操作的其实是总日志。)

到这里其实已经可以达到写入任意字符串的目的,但还不能说明什么,因为写入的地址并不能被访问,而且文件名是固定的,所以直接上马可能性不大,得找地方利用这个允许我们随意写东西的地方。

看回刚刚爆破开的admin.php,在登陆验证逻辑过后紧接着这一段

image-20250318194822271

可以看到它把log文件读到变量$action_log,接着$log_lines再以换行作为分隔符将日志作切割。这里我们再看回到config.php,来看一下日志是怎么写的。

image-20250318195738488

可以看到$log_file变量就是以换行为结尾的,并且以逗号为分割。所以我们继续往下看admin.php。可以发现我们写入的东西都会被处理完后放到对应的变量里,而只有$additional_info是我们可以控制的($encoded_user是base64,控制不了)。

接着往下看

image-20250318200343405

这段代码将$encoded_user作为用户名拼接路径,然后再把路径的内容读出来。那么利用思路就清晰了,我们刚才一直在找的任意写入应该就是在这里发挥用处,也就是控制$encoded_user。但这不就和上面说的不能控制矛盾了吗?有什么办法可以通过$additional_info来控制$encoded_user吗?

如果你的思维足够跳跃,应该就能想到用任意文件写入来写入换行,以此在$additional_info来创建一条假日志。这样我们就能控制这条假日志的用户名了。说起来有点抽象,我们来看个具体的例子

这是根据log_action函数生成出来的日志,大概像下面这样

1,abwidab,admin_login,1,\n //log_action($username, 'admin_login', 1);
2,dqbvwwfqiq,register,1,\n //log_action($username, 'register', 1);
3,wqifbqwibc,record_banned,0,Failed to record banned content\n //当record_banned无法创建目录但可以创建文件时log_action($username, 'record_banned', 0, 'Failed to record banned content');
4,wqifbqwibc,record_banned,0,Failed to create record directory for 这里拼接任意字符串\n //当record_banned无法创建目录但可以创建文件时log_action($username, 'record_banned', 0, 'Failed to create record directory for '.$username);

那么username应该输入什么呢?示例如下

2,dqbvwwfqiq,register,1,\n
4,wqifbqwibc,record_banned,0,Failed to create record directory for \n
11,../flag,record_banned,1,111\n

而我们输入的用户名就是

\n
11,../flag,record_banned,1,111\n

这样以来,我们这条假记录被读取后,$encoeded_user就是../,读取路径就变成/var/www/banned/../flag。这样就能成功读取了。

好的,知道要怎么写了,理论上就只要回去注册页面注册个这个用户名的新用户就能成功。但这里还有个细节,那就是这条假日志的创建是要在banned文件夹创建失败时才能写入,要怎么样才能它创建不成功呢?很简单,mkdir函数无法创建两层路径,而我们的路径名又是base64的结果,base64字符集里有/,所以就有可能创建出两层目录。因此我们的用户名要即满足路径穿越,又满足文件创建失败。

image-20250318203933588

修复就比较简单了,只要把$username变量拼接删掉就行

blog

队友写的:https://r1.pub/p/ciscn-ccb-2025-wp/

ISW

CCB2025

更新ing