目标网址:https://user.wangxiao.cn/login?url=https%3A%2F%2Fwww.wangxiao.cn%2F
案例目标:网页账号密码模拟登录,获取对应的题库内容。
网页分析
问题复现,随便输入账号、验证码和密码,然后点击登录。
通过抓包查看请求内容。
从返回的结果可以看出,用于处理登录的数据包传输的password
为加密格式,发送的是POST
请求。
我们想要模拟这个登录请求,必须要做的一个事情就是分析该密码的加密逻辑,将自己登录传输的密码按照相同的加密逻辑进行加密,然后传输过去进行验证。
接下来开始分析其加密逻辑,点击启动器。前三个请求都是使用jquery
发送。
我们直接从第四个开始分析,点进去,打上断点。
再次点击登录按钮,让其在断点处暂停。
从e
中的url
可以看出,这个时候请求的地址为/common/getTime
。我们释放此次断点。
这个时候的url
地址才是正常的。
查看发送的Ajax
请求中的data
值。
通过观察发现data
的值是通过i
赋值得到,在此处的密码已经被加密,我们需要往前找。
在此处可以知道i
是通过e.data
来赋值得到,并且e
中的password
已经的被加密的,而e
是通过函数传输过来的,这个时候就需要往前找调用这个函数的地方,传输过来的e
是什么,查看前一步操作。这个时候就需要借助右下角的调用堆栈。
在这个地方是通过zdAjax
接口对其进行调用,并且传入接口中有两个参数,第一个是param
其作用就是将加密的数据传输过去;第二个是一个箭头函数,其作用暂时不清楚。再次利用右下角的调用堆栈,回到之前的调用栈,搞清楚箭头函数传输过去是做了什么事情。
经过分析可以看到,这个箭头函数传输过来只在Ajax
传输后对其进行了使用。
小结,zdAjax(param, res => {})
作用为将param
作为Ajax
请求的参数发送到服务器的"/apis/" + param.url
处,然后将服务器接收param
参数后返回的数据,再传输作用再第二个箭头函数中。
再回到后一个调用栈,现在可以知道,e
是通过该数据包中的param
参数赋值得到,并且在这个地方param
也是被加密的,继续向上找。
往上找发现param
参数中的password
是通过encryptFn(pwd + '' + ress.data)
该方式对密码进行加密,此处pwd
即为实际输入的未加密的密码,而param
中已经被加密了,说明encryptFn
即为加密方法。
其中ress.data
的值为ress
传输过来的,而经过前面的分析zdAjax
接口的作用是将paramss
作为参数发送到服务器中的某个url
地址,然后将返回的数据传输给第二个箭头函数。
换句话说,ress
的值为paramss
发送给服务器后的响应值。再网上找paramss
看看是什么。
可以看出,服务器接收参数的地址为/common/getTime
,并且没有传输任何参数。
接下来看看加密方法encryptFn
。
点击来就可以看到关键字setPublicKey
说明此处密码的加密方式就是RSA
加密,并且公钥就再"xxxx"
中。
PS:在分析网页的时候我们可以很明显的看到很多keepOurCookie12
关键词,这说明该网址很有可能对网页的Cookie
校验非常严格,可能调用JS对Cookie
进行处理,这可能导致我们使用传统的session
会话去保存登录状态无法解决登录问题。
现在加密方式和公钥都有了,顺利的话密码的加密问题应该已经可以解决。
除了账号和密码,在登录的时候还有一个验证码图片需要处理。查看验证码的请求方式。
图片的请求是使用的POST
请求方式,
API地址为https://user.wangxiao.cn/apis//common/getImageCaptcha
接下来查看其返回的内容,点击预览。
可以看到图片的传输使用的是base64
进行编码传输的。 我们可以将其获取下来,将其反向转化为字节后存储到本地即可看到正常图片内容。接下来可以人工识别或者调用第三方工具识别。
现在验证码和密码问题都已分析完成,开始进行代码编写。
代码编写
验证码识别
验证码识别问题相对简单点,我们先解决该问题。
直接请求API
发现无法得到正常的图片数据。
给其增加常见的请求头。
从结果可以看到,加了常见的请求头参数仍然无法解决问题。
通过在其发送Ajax
请求时可以看到,在发送Ajax
请求时有一个很特殊的参数contentType
也被传输过去了。
然后我们观察在请求头中也有这个参数,尝试把它加到请求头中进行访问。
这个时候发现能够得到JSON
数据了,但是服务器的响应时间明显增加了,并且数据还是不对,显示未知错误。
这个时候时候通过观察cookie
我们发现在其中有一个参数为sessionId
值为1683532405248
,这很有可能是一个与时间有关的参数,时间间隔太长导致请求失效了。
这个cookie
是从哪里来的呢?我们关闭断点,直接刷新整个页面(注意:需要先清楚网页缓存),观察下在请求首页时候的cookie
。
可以看到在请求首页的时候,响应头中有个Set-Cookie:sessionId=1683545006624; path=/
参数,其中就有我们想要的sessionId
,该参数的作用就是给后面的访问设置Cookie
。
这个时候我们可以点击密码登录查看验证码数据包Cookie
中的sessionId
值看看是否一致。
通过比较可以发现确实是一致的。
所以要想请求到正确的图片数据,需要先请求首页让其设置Cookie
后,再请求图片API
。这个时候就需要使用到会话Session
机制。
这个时候发现请求结果正常。使用base64
对其进行解码存储到本地。
图片显示正常。有两种常见的方式对其进行识别:
- 人工识别后输入
- 调用第三方工具识别
在这里我们采用调用第三方工具的方式进行识别。
调用的是图鉴的API
。在其官网有对应的调用示例,我们直接复制代码即可。
1 | import base64 |
其中:
uname
:用户账号pwd
:用户密码img
:用户图片地址typeid
:识别的图片种类,在官网可以查看,我们这里是数字和英文的组合即为3。
按照要求填入内容。
成功返回结果,验证码识别完成。
密码RSA加密
经过前面的分析,再进行密码加密的时候不是单纯的对密码加密,会加上一个ress.data
这个参数是通过对服务器中的/common/getTime
地址进行请求返回的结果。在抓包工具中也可以看到该数据包。
使用POST
请求该数据包,同样在请求头中加上Content-Type
。
结果返回正常。
筛选出get_time
值,并为了后面代码编写,先定义好账号和密码(自己填写)。
拼接好待加密参数
经过前面的分析我们已经拿到RSA
公钥,接下来使用RSA利用公钥对其进行加密,并将加密后的数据转化为Base64
。
1 | MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDA5Zq6ZdH/RMSvC8WKhp5gj6Ue4Lqjo0Q2PnyGbSkTlYku0HtVzbh3S9F9oHbxeO55E8tEEQ5wj/+52VMLavcuwkDypG66N6c1z0Fo2HgxV3e0tqt1wyNtmbwg7ruIYmFM+dErIpTiLRDvOy+0vgPcBVDfSUHwUSgUtIkyC47UNQIDAQAB |
加密成功,现在已经解决了在请求时需要使用到的所有参数。
开始发送登录请求
在发送请求时,注意将data
表单转化为JSON
格式。这个在JS
源码中也可以看到,在获取到e.data
时调用了一个JSON.stringify()
方法。
这里登录成功了,我们是否可以直接使用session
会话请求到其他页面内容呢?
例如我们请求某个模拟考试(http://ks.wangxiao.cn/TestPaper/getPaperRuleQuestions),看是否能够拿到试题。
发现无法正常请求该地址,又自动跳转到了登录页面。
说明我们在此处通过session
保存的cookie
并没有保存到登录状态。这个就是我们前面说的该网站在处理Cookie
时调用了JS对Cookie
进行进一步处理。
对于这种情况只能通过分析网页JS
后手动补全Cookie
。
按照JS
中写的手动补全Cookie
。
数据获取成功。
完整代码
完整代码如下,其中账号密码需要手动填入。
1 | import json |
tujianAPI.py
1 | import base64 |