一次检测引发的对随机数的探讨

作者:@nick
来源:90WiKi
发表时间: 2013-6-14 19:48:51

写在前面

渗透过程毫无亮点,求不喷。没有牛逼的技巧,just for fun。

开始

目标网站是aspx的,用的是商业源码。登陆系统,找到上传的地方,看源码。

if(fileext!=".rar"&&fileext!=".rar"&&fileext!=""&&fileext!=".doc"&&fileext!=".xls"&&fileext!=".ppt"&&fileext!=".pdf")
        {
            alert("请选择rar,doc,xls,ppt,pdf文件");
            return false;
        }

发现用js来判断后缀。用burp抓包改包秒杀之。

打包了源码,拖了库,留了个后门。上了几个提权杀器,被杀了。服务器装的是eset endpoint security。简单加壳,过了杀软。溢出,抓管理员密码。Ip在内网,之前在同类机器上碰到eset endpoint security,可能会拦截出站请求,怕把事情搞大,就没转发3389,然后就悄悄离开了。

几天后,该站发出公告,说是系统发现重大漏洞,需要维护。我猜是之前不免杀的提权被杀软杀后,管理员看到了提示,发现了侵入痕迹。事情还是被搞大了,担惊受怕了好几天。一个星期后,网站上线,没有追究我的责任。粗略看了下,代码被改过了,上传部分的漏洞没了,留的后门也被删了。因为没什么需求了,就任之而去了。

到了后来,因为要用到网站的接口,但是该网站对普通权限用户做了限制。此时萌生了二次入侵的想法。
重新找上传点,发现还是用js做的后缀验证,可以上传任意文件,并能用文件流的方式下载到上传后的文件,目测是读入文件流然后送往输出流。上产后的文件被改名为随机字符串,如2b538465-8ff9-4ee5-89cc-d9e71495f267.asp。现在的问题是,传上去的文件找不到路径。

依稀记得之前上传后的文件夹为/uploadfile/。尝试/uploadfile/2b538465-8ff9-4ee5-89cc-d9e71495f267.asp,提示404。

没办法了,只能去翻之前的代码,看看能不能找到与上传文件相关的东西。

从上传页面的aspx文件,找出对应的dll,然后用reflector反编译。.net的文件反编译出来的代码可读性还是蛮高的~
产生文件名的关键代码是

Random __gc* random = __gc new Random();
str = String::Concat(HttpContext::Current->Session[S"UserId"]->ToString(), random->Next(0x186a0, 0xf423f)->ToString(), str3);

其中str3代表文件后缀。其算法是UserId后加上100000到999999的6为随机数字。
也就是说,可以进行爆破出来。

学术时间

渗透重要的一点是不能过早的暴露自己,我觉得,在本地花大段时间换一个在服务器上较小被发现的概率是值得的。

如果单纯的猜解的话,最差运气要900000次,暂时把它看成1000000次。此时有两种减少爆破猜解次数的方法。

第一种方法,靠增加上传的asp木马文件个数来减少猜测次数。1个asp文件最多要1000000次猜测,2个asp文件最多要500000次猜测,4个asp文件最多要250000次猜测…

我们可以看到,从一个文件增加到4个文件,可以大大减少需要猜测的次数。从服务器的日志看,上传一个asp木马是一次请求,猜测一次文件也是一次请求。

但实际上,两者还是有差别的。给上传和猜测分别设置权重。设上传的权重为10,猜测的权重为1。即猜10次相当于上传一次asp木马。这样,我们可以得出最优的上传次数与猜测次数。经过一系列的数学计算…

image

第二种方法,既然是靠随机数产生文件名,大家都知道,计算机中的随机数都是伪随机,靠一个种子经过一系列运算来得出一串数值罢了。这样,伪随机就有被猜测到的可能,如果方法得当,可以缩减猜测的范围。
去msdn上看c#中对随机数的定义。

构造函数Random()默认种子值是从系统时钟派生而来的,具有有限的分辨率。 因此,通过调用默认构造函数而频繁创建的不同 Random 对象将具有相同的默认种子值,因而会产生几组相同的随机数。

也就是说,如果本地和服务器上产生随机数的时间一致的话,可能会出现相同的随机序列。服务器的时间可由http响应头中得到。不过http响应头中的时间为GMT格式,需要做下转换以相互对应。

为了测试这个想法,特地安装visual stadio 2012,从网上找了一段代码来进行测试。

public static string ToGMTString(DateTime dt)
{
        return dt.ToUniversalTime().ToString("r");
} 

static void Main(string[] args)
{
        for (int i = 0; i < 100000000; i++)
        {
                Console.Write(DateTime.Now.TimeOfDay.ToString());
                Console.Write(" ");
                Console.Write(ToGMTString(DateTime.Now));
                Console.Write(" ");
                Random r = new Random();
                Console.WriteLine(r.Next(0x186a0, 0xf423f).ToString());
        }
}

编译成功后,本机开两个进程运行,并把输出重定向到txt文件中。

可以得出两个结论。一是接近的时间点取随机数是会重复的,二是两个进程取随机数的时间相同或接近的话,可能取到相同的随机数。

如果在两台不同的电脑上运行呢?

结论是两台不同的电脑,即便时间接近产生的随机数也是不同的。

image

这不科学啊!

反编译random类,发现构造函数。

public: Random();

public: Random(Int32 __gc* Seed);

其中

public: Random() : this(Environment::TickCount)
{
}

也就是说,Random()最终还是调用的Random(Int32 __gc* Seed)

追溯Environment::TickCount

public: __property static Int32 __gc* get_TickCount()
{
return Environment::nativeGetTickCount();
}

搜了下nativeGetTickCount()这个函数,

该函数从0开始计时,返回自设备启动后的毫秒数(不含系统暂停时间)

详见 http://www.cnblogs.com/jxsoft/archive/2011/10/17/2215366.html

那么,假设服务器的开机时间为1天到 30天之间。那么,所需爆破的次数约为

1000*60*60*60*24*30=155520000000=155.52G

按照目前计算机的性能,应该花不了多长时间的。

但目前的情况,对于我们来说,是不知道服务器运行了多长时间的。我们可以利用的方式只能是先获得一个系统产生的随机数,然后通过爆破,得到当时系统运行的时间,并记录下服务器响应的时间。然后发送一次请求,记录下响应时间,推测出此时系统运行的时间,然后本地产生一小段时间内的随机数,进行爆破。

此时涉及到一个概率重复的问题。设随机数的取值范围为n,如果n太小,在产生完一组随机数之后极有可能出现很多重复的数值,这会影响到对系统时间的判断。这时候需要在服务器上产生多个有时间关联的随机样本,才能以更高概率推测出服务器的当前运行时间。需要的样本个数与猜错的概率成反比。

或者可以尝试DDOS使服务器重启,以减少猜测次数以及降低猜错概率~

(只是猜想,未经测试,本人不负责)

后续

Ok,回到渗透上。为了减少发包的大小,采用了发HEAD包而不是发GET包的方式来探测资源是否存在。选用burp的intruder功能,调整线程数,调整发包时间。爆破了一段时间,愣是没有结果。

因为已经开始爆破,服务器上必然已经留下日志了,这时只能成功不许失败。有种风萧萧兮易水寒,壮士一去兮不复还的悲壮。

回头去找网站维护之前我上传过的一个合法文件,Myuid760682.doc。访问/uploadfile/ myuid760682.doc,也显示404。但这个文件的确还存在,/uploadfile/这个目录也存在。看之前的源码,/uploadfile/这个目录也的确是用来存储上传文件的目录。

思路卡壳了。

这时想到之前拖过库,有管理员密码。屁颠屁颠登陆,在添加文章处找到上传点,js验证,可以上传文章中图片,这个图片的地址也能找到,文件夹为/ImageData/。

上传asp一句话,404。上传cer后缀的一句话,404。上传aspx一句话,菜刀连接不上,不过这次状态是200。尼玛,在耍我么?

直接上传aspx大马,登陆成功。查看web.config,发现是对目录做了登陆权限验证。目前浏览器是有管理员cookie的,所以能打开大马,而菜刀没管理员的cookie,所以不能正常工作。而本网站设置对asp不解析…从web.config中,得到网站改版后,上传的文件全都保存在网站目录之外,怪不得怎么也找不到上传之后的文件。

下了一份新源码,反编译相关文件,发现自己想要的数据是在第三方服务器上,用网页接口的方式给出。既然有源码了,按照调用原理,自己写了个调用程序,得到数据~

image

结束

到这里目的达到了,按理应该结束了。但是手贱又多测试了一下。

用aspxspy的命令执行功能,ping 127.0.0.1,正常结果返回,ping同一网段和相邻网段服务器,正常结果返回,ping 8.8.8.8,超时,Ping www.baidu.com,无法解析。也就是说出站的icmp协议和dns协议包都在路由器端被拦截了。Aspxspy自带的端口转发失败,用reDuh.aspx尝试端口转发,连接reDuh.aspx失败,客户端卡死。网站能去远程站点(外网ip,不在相邻网段)调用接口,但是ping不通。猜测是路由器做了一些规则吧。

至此结束。