关于 HTTP 我不讲理论,只讲一下具体的用途。
GET 请求之发送验证码
首先我举一个例子,收过短信验证码吧,一般来说在你注册账号的时候就会用到,会有一个点击发送验证码的按钮,这里以 网址 114 预约挂号 为例
输入完手机号,点击获取验证码就能收到验证码,但这背后的原理又是啥,服务器那边怎么知道我要验证码,并且我输入正确的验证码就进入,错误的就不行。而这正是网络协议 HTTP(关于 HTTP 相关的这里不做过多讲述,希望读者能自行百度了解),我先说下点击了获取验证码发生了什么,通过抓包工具可以获取到如下请求
GET https://www.114yygh.com/web/common/verify-code/get?_time=1601308153790&mobile=15212345678&smsKey=LOGIN HTTP/1.1
Host: www.114yygh.com
Connection: keep-alive
Accept: application/json, text/plain, */*
Request-Source: PC
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3775.400 QQBrowser/10.6.4208.400
Content-Type: application/json;charset=UTF-8
Referer: https://www.114yygh.com/
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
首先我们向服务器发送了一个如上的请求,这是一个 GET 请求,同时请求的链接(url)为https://www.114yygh.com/web/common/verify-code/get?_time=1601308153790&mobile=15212345678&smsKey=LOGIN
如果你会点英文的话,可能会理解其中的含义,主要就这几个参数verify-code
验证码,_time=1601308153790
时间戳(时间戳是一个记录时间的东西,用当前时间减去1970-01-01 08:00:00
即可得到,你可以通过这个工具时间戳在线转化,这里的1601308153790
所对应的时间为2020-09-28 23:58:06
)还有一个mobile=15212345678
,这个15212345678
是我刚刚输入的手机号。这是向服务器请求的数据,那在来看看服务器返回给我们的是什么
HTTP/1.1 200
Date: Mon, 28 Sep 2020 15:49:15 GMT
Content-Type: application/json;charset=UTF-8
Content-Length: 57
Connection: keep-alive
Set-Cookie: hyde_session=Kd10cra3X4yNBePaaQTKUkuYgX9J6Hfx_5337693
Set-Cookie: hyde_session_tm=1601308154470; Domain=.114yygh.com; Path=/; HttpOnly
Content-Security-Policy: : default-src *.114yygh.com *.qq.com *.baidu.com; font-src * data:
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
X-Frame-Options: SAMEORIGIN
X-Via-JSL: d8c5e31,-
X-Cache: bypass
{"resCode":0,"msg":null,"data":{"endMilliseconds":59997}}
只需要关注最后一行即可,其中 resCode 为 0,同时手机号15212345678
也收到了验证码,貌似 resCode 为 0 就决定了服务器是否有给手机号发送短信,事实上也是的,那么说了这么多,有什么用呢,用处可大了。
既然这样,我知道了 发送上面的那个请求服务器就能给对应的手机号发送验证码,那么我能不能将上面那个请求的手机号给改一下,改成15287654321
,事实上是完全没问题的,这里我就放一张 HTTP 测试工具的截图。
那么是不是我多请求这样像服务器请求,我就能源源不断的收到验证码,现实很美好,人家服务器也不傻,我再一次向服务器发送请求,服务器给我的结果是
{"resCode":10000,"msg":"请58秒后重试","data":null}
没错,就需要等,而且这里的 resCode 也不为 0,那么既然要等一分钟的话,我能不能写个定时脚本,每隔一分钟发送一次,人家服务器也不傻,一般来说,一个手机号最多也就收 5 次验证码,多了就会提示明天再重试,或者今天收到的验证码过多等等。而外面的炸则是通过收集几百个这样的请求,然后将手机号替换成要轰炸的,即可实现多平台验证码轰炸一个手机号。
现在你可能已经知道了初步了解 HTTP 请求,但一般的网站都不会像这个这么简单的,明文标码,通常都会进行效验,例如图片验证码,滑块,点字,点图等等,并且还会进行加密操作处理,而这才算真正的难点。
POST 请求之登录
既然发验证码是这样,那如果是登录呢,下面就用网站 万创帮 为例,首先进入登录界面
输入手机号和密码,点 击登录,同样的我们可以通过抓包工具获取到对应的 HTTP 请求,如下
POST https://m.wcbchina.com/api/login/login?rnd=0.6463111465399551 HTTP/1.1
Host: m.wcbchina.com
Connection: keep-alive
Content-Length: 149
Pragma: no-cache
Cache-Control: no-cache
Accept: application/json, text/javascript, */*; q=0.01
Origin: https://m.wcbchina.com
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Mobile Safari/537.36
Content-Type: application/json
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
{"auth":{"timestamp":1601312429287,"sign":"777473FB2A1838DBD64BA7A11C98911B"},"username":"15212345678","password":"E9BC0E13A8A16CBB07B175D92A113126"}
貌似比上面那个发验证码的复杂,确实,这是一个 POST 请求,你在链接上看不出什么有效信息,在最后一行才是关键。这里的timestamp
也就是时间戳,记录时间的,username 是我们输入的手机号(账号)没什么问题,这里的 sign 和 password 的内容又是啥?这就是加密,让你不能简单单纯的通过替换文本来实现登录,这就来分析一下他到底怎么加密的。
通过在浏览器按下 F12 键,打开控制台面板,接着点击 Network,这里会将我们发送的请求记录下来
同时鼠标停在 Initiator 上有如下结果
不管三七二十一,点击跳转到对应的代码先,然后在左边下一个断点
这时候我们在点击登录按钮看看
没错,浏览器这时候停了下来,停在了我下断点的地方,通过函数名也可以猜到这个是发送的,对应的肯定在上面,通过右边的 Call Stack 函数调用栈即可追随上一函数
在这里我看到了原文的信息,这是通过 Jquery 通过 id 获取元素的值,也就是这里的手机号和密码,在这里还都是原文,点到下一个函数则变成了密文,那么肯定是上一个函数做了手脚。
认真观察,N 这个是我们的密码,但对 N 进行了一个操作也就是 a.hex_md5(N)
,没错,这就是 md5 加密。有关加密的可以看看我写过的 浅谈加密算法
那么通过加密工具将 md5 加密是否能得到我们要的加密结果,如下
E9BC0E13A8A16CBB07B175D92A113126
在看看 Password 的值,一模一样,看来已经解决了一个参数,那么还有一个sign
呢。貌似右边的函数调用栈都不好使,我试试搜索字符串 sign 看看
好家伙,直接定位到了,那么同样的在这里下一个断点,查看一下到底发生了什么(实际上 js 静态分析就完事了,这个网站太简单了)
这里的 N 看来就决定了 sign,而 N 也是通过 md5 加密的,不过原文我还不知道,让代码执行到这一行看看结果
这里的 c 就是时间戳,而 token 和 password 都是未定义,那么就好办了。这里明文也就是 c 1601312429287
,那么用加密工具即可得到777473FB2A1838DBD64BA7A11C98911B
,那么参数都搞定好了, 只需要替换一下账号,然后将正确的密码通过加密算法(这里为 md5)生成,同时对 sign 也生成出来,然后提交给服务器就能收到我们登录的请求,就认定为我们登录了,记录为在线用户。
如果我用的加密算法错了,或者我分析错了,提交给服务器会是怎么样的
例如我这里的 sign 算法是错了(将结尾的 B 改成了 A),发送给服务器,服务器返回给我们则是失败的结果,原因很简单,就是为了防止别人恶意登录所添加的效验,提交的数据伪成败,就决定了服务器给我们结果是成败。
总结
通过上面的一些例子,只要能伪造请求,发送给服务器,就能获取我们想要的结果或者目的,事实上也是如此,但伪造数据的难易则要由所对应的网站而定,有的网站压根就没没什么难度,而有的你搞一天都未必能搞的出来。如今的网站在这方面也都下足了功夫,想要轻松的伪造请求可不是件容易的事情了。
也正是因为我学了 HTTP 请求与 JS 逆向分析,我能做的也就更多,而正是基于 HTTP 协议下,其中一个就是有关于超星刷课软件的例子,如果我没有学过这些,我就不可能写出来。
后续会有关 HTTP 请求这方面都会放在逆向这个分类下,比如一些网站的加密算法和常见的坑等等。