在 buuoj 上看到的这个比赛题目,期间平台关了,就拿了 Dockerfile 本地做了,web 题目感觉还不错

encode_and_encode [100]

  • 打开靶机,前两个页面都是 html 页面,第三个给了页面源码

  • 源码如下

<?php
error_reporting(0);

if (isset($_GET['source'])) {
    show_source(__FILE__);
    exit();
}

function is_valid($str) {
    $banword = [
      // no path traversal
      '\.\.',
      // no stream wrapper
      '(php|file|glob|data|tp|zip|zlib|phar):',
      // no data exfiltration
      'flag'
    ];
    $regexp = '/' . implode('|', $banword) . '/i';
    if (preg_match($regexp, $str)) {
      return false;
    }
    return true;
}

$body = file_get_contents('php://input');
$json = json_decode($body, true);

if (is_valid($body) && isset($json) && isset($json['page'])) {
    $page = $json['page'];
    $content = file_get_contents($page);
    if (!$content || !is_valid($content)) {
      $content = "<p>not found</p>\n";
    }
  } else {
    $content = '<p>invalid request</p>';
  }

// no data exfiltration!!!
$content = preg_replace('/HarekazeCTF\{.+\}/i', 'HarekazeCTF{&lt;censored&gt;}', $content);
echo json_encode(['content' => $content]);
  • file_get_contents('php://input') 获取 post 的数据,json_decode($body, true) 用 json 格式解码 post 的数据,然后 is_valid($body) 对 post 数据检验,大概输入的格式如下

  • is_valid($body) 对 post 数据检验,导致无法传输 $banword 中的关键词,也就无法传输 flag,这里在 json 中,可以使用 Unicode 编码绕过,flag 就等于 \u0066\u006c\u0061\u0067

  • 通过检验后,获取 page 对应的文件,并且页面里的内容也要通过 is_valid 检验,然后将文件中 HarekazeCTF{} 替换为 HarekazeCTF{&lt;censored&gt;} ,这样就无法明文读取 flag

  • 这里传入 /\u0066\u006c\u0061\u0067 后,由于 flag 文件中也包含 flag 关键字,所以返回 not found ,这也无法使用 file://

  • file_get_contents 是可以触发 php://filter 的,所以考虑使用伪协议读取,对 php 的过滤使用 Unicode 绕过即可

  • 可以看出,json 在传输时是 Unicode 编码的

Avatar Uploader 1 [100]

  • 给了源码,打开靶机,登录之后,是一个文件上传

  • 首先 config.php 中定义了一些常量

  • 然后在 upload.php 中判断文件大小,并使用 FILEINFO 判断上传图片类型,上传图片只能是 png 类型

  • 后面再用 getimagesize 判断文件像素大小,并且再进行一次类型判断,如果不是 png 类型就给出 flag

  • 在这两种判断上传图片类型的函数中,有一个很有趣的现象, FILEINFO 可以识别 png 图片( 十六进制下 )的第一行,而 getimagesize 不可以,代码如下

<?php
$file = finfo_open(FILEINFO_MIME_TYPE);

var_dump(finfo_file($file, "test"));

$f = getimagesize("test"); 
var_dump($f[2] === IMAGETYPE_PNG);
  • 结果,16进制文件也在下面

  • 直接上传这个文件就可以获取 flag 了

Easy Notes [200]

  • 给了源码,打开靶机,是一个笔记系统

  • 在登陆处进行了匹配,只允许输入 4 到 64 位规定字符,且不是前端验证

  • 登陆成功后,可以进行增删查和导出为 zip 或 tar 的功能,点击 Get flag 提示不是 admin

  • 既然拿到源码就先看看全局配置 config.php ,就写了一行,定义临时文件目录

define('TEMP_DIR', '/var/www/tmp');
  • 进入 page/flag.php 看一下给出 flag 的条件,要满足 is_admin() 函数

  • 跟进 is_admin() 函数,没有发现什么可以利用的地方

  • 看到有个导出功能,它会将添加的 note 导出为 zip,这个文件存放的位置在 TEMP_DIR ,和 session 信息保存在同一个位置,那么是不是可以考虑伪造 session

  • session 文件以 sess_ 开头,且只含有 a-zA-Z0-9-

  • 看到 $filename 处可以满足所有的条件

  • 构造 usersess_type. ,经过处理之后,$path 就是 TEMP_DIR/sess_0123456789abcdef 这就伪造了一个 session 文件

  • 然后向这个文件写入 note 的 title

  • php 默认的 session 反序列化方式是 php ,其存储方式为 键名+竖线+经过serialize函数序列处理的值 ,这就可以伪造 admin

  • 在最后,它会将构造的 $filename 返回,这样就可以拿到构造出的 admin 的 session 数据

点击收藏 | 0 关注 | 1
  • 动动手指,沙发就是你的了!
登录 后跟帖