WEB|[Zer0pts2020]Can you guess it?

发布时间 2023-05-06 14:57:03作者: scarecr0w7

源码

<?php
include 'config.php'; // FLAG is defined in config.php

if (preg_match('/config\.php\/*$/i', $_SERVER['PHP_SELF'])) {
  exit("I don't know what you are thinking, but I won't let you read it :)");
}

if (isset($_GET['source'])) {
  highlight_file(basename($_SERVER['PHP_SELF']));
  exit();
}

$secret = bin2hex(random_bytes(64));
if (isset($_POST['guess'])) {
  $guess = (string) $_POST['guess'];
  if (hash_equals($secret, $guess)) {
    $message = 'Congratulations! The flag is: ' . FLAG;
  } else {
    $message = 'Wrong.';
  }
}

源码分析

if (preg_match('/config\.php\/*$/i', $_SERVER['PHP_SELF'])) {
  exit("I don't know what you are thinking, but I won't let you read it :)");
}

访问路径不能以/config.php/结尾

$_SERVER['PHP_SELF']):当前正在执行脚本的文件名;PHP中$_SERVER的详细用法
http://123.com/index.php --> index.php
http://123.com/index.php/config.php --> config.php

if (isset($_GET['source'])) {
  highlight_file(basename($_SERVER['PHP_SELF']));
  exit();
}

GET方法获取到source参数就显示显示文件内容,这里使用basename()存在漏洞,代码提示flag在config.php文件,可以利用这里查看config.php文件

basename():返回路径中的文件名部分
basename() 在使用默认语言环境设置时,会删除文件名开头的非 ASCII 字符,%ff、%2b、%0d等,中文内容也可以:汉字、?、《、》、;等

$secret = bin2hex(random_bytes(64));
if (isset($_POST['guess'])) {
  $guess = (string) $_POST['guess'];
  if (hash_equals($secret, $guess)) {
    $message = 'Congratulations! The flag is: ' . FLAG;
  } 

随机生成字符并转为16进制,POST方式获取guess,guess值与secret值相比较,如果相等显示flag,但是这里使用的是hash_equals避免了时序攻击和 php 弱类型,无法利用

hash_equals():用于同时比较两个字符串是否相等,无论字符串是否相等,函数的时间消耗是恒定的,可以有效的防止时序攻击

解法

利用basename()读取config.php的内容,$_SERVER['PHP_SELF']就要等于config.php,但是preg_match禁止以config.php结尾,这里就利用到了basename()漏洞加字符绕过
payload

/index.php/config.php/字?source

这里/index.php/config.php,虽然$_SERVER['PHP_SELF']获取的值等于config.php,但是浏览器可以解析为index.php从而正常显示页面
构造请求,得到flag

flag{e8b0c7d6-f24c-4227-9e4e-445dfea96b22}