本帖最后由 jidesheng6 于 2020-3-19 00:05 编辑
为什么写这篇文章?
- 更多的是记录这次分析过程,尽管很简单
- 网课真的挺烦人,学的东西也没啥用处
我是怎么做的-刷网课时长 AND 刷课程点击
首先来看一下学校网课平台的页面,很简陋【因为隐私问题网站不会在这篇帖子中展示,包括任何出现的人名等信息】:
**一开始我是想尝试分析一下登录的【登录的我分析了最后,但是没有成功,后面会提到他的登录流程】,看页面那么简陋,就去看看课程那边抓包能不能抓到啥吧,刚进到下面这个页面我就有点愣住了:
不知道他的判断机制是什么,看到这个PDF文件的小箭头我就打算用Jquery选择器选一下这个next执行一个click动作吧:
发现他是可以的,于是就了个循环,来执行这个操作,自己看到页数就可以了,最开始调试就是不用代码获取,自己写死了:**
var a;
for(a=0;a<10;a++)
{
$("next").click();
}
**等我执行完再返回课程列表看学过的课,我发现他是这样的:
仔细想了一下,难道说看完就可以了吗,我都没有仔细看,于是我又点进去了一个文件,但是这次我没有看,直接返回,发现系统显示我已经学完了,那我就明白了:点进去=学过
按照这样来说的话:只要我们在当前课程页面把所有的PDF文件都点击一次不就可以了吗【其实我第一次没有想到这个问题,我接下来是去分析了视频来着】,
后来又去看了一下视频,发现视频也是这样,但是后台如果记录下来怎么办呢,因为视频是有播放时长的,你当前播放到哪里他都是有记录的。
之前有看过别人写的文章,里面提到有些视频通过后台提交当前播放位置来记录所学位置,所以我打开了调试器查看我们学校是不是也是这样来判断的:
点击播放没多久,在调试器中就出现了一条新的请求,于是看一下是什么东西:
在图中我注意到一个很显眼的参数:timepoint,字面翻译过来叫做时间点,于是我就看了一下当前视频播放到哪里了**
**上图可以看到,大概是2秒,但是我也不确定,又等了会儿,出现了新的请求,比之前多了几秒,这里不放图片了哈,发现是和当前播放位置对应起来的
所以猜测这条URL携带的timepoint就是提交给服务器当前用户视频播放的位置,于是我放在浏览器中进行请求,返回结果和后台自动发送的返回是一样的,
同时这个也让我注意到了一个细节:在刚才的请求中,比较关键的就是课程ID和当前的文件ID,在请求参数中没有发现任何用来鉴别用户的参数【我自己没有系统学过这些服务器认证用户的知识,后来查资料知道的,那个cookie中的jessionid就是客户端和服务器保持联络的玩意,用那个鉴别用户】,所以我猜测,也许不需要进行登录模拟了,直接把头部cookie带上说不定就可以了,过期了大不了我再获取一次
之后返回刷新可以看到网课的上次保存时间【这个图很早了,这个方法好像失效了,刚才没成功,找了个以前成功的来凑着看】,可以看到记录的时间可以随便更改
但是目前是失效了,后来无聊的时候又想到了用JavaScript控制不就可以了吗,设置当前进度条位置:**
var e = document.getElementById("example_video_1_html5_api")
e.currentTime=1000
**也是可以的
我原本以为我做完这些我课就可以不用管了,当我回到课程主页看到这个的时候心态有点崩了:
他有一个学习的总时长,我当时的时间是32分钟,规定的两门课一门是240分钟还有一门是230分钟,一开始天真的想用Fidder更改这边显示的时间,然后等他把数据一起交给服务器,现在看起来有点傻,等我能看到时间的时候服务器就已经知道我有多久了,到时候也是不会把本地这个带上去,带的根本不是这里的,应该是我在学习页面多久他就给服务器提交我在的时间,于是我就想,这样说来是不是也和视频时长一个原理呢。
于是打开一个PDF,继续抓包【因为PDF没有播放时长什么的】,打开PDF之后开启网络抓包,等待了一会儿第一条包出现:
又等了一会儿,发现有多条数据包在发送,时间好像还是挺有规律的,不管三七二十一,我也不知道是不是,用python写一个小demo试试看就知道了**
#--* coding:utf-8 --*
import requests,time
'''
网课刷时长脚本
/@author:jidesheng
/
'''
CourseHeartUrlList = ["url?playStatus=false&isResourcePage=true&courseVersionId=4db484e151c74cf486cb641f43d79d2a&activityId=01c34bcf2e1e11ea85dd005056b13345&type=1&isStuLearningRecord=2&token="
,"url?playStatus=false&isResourcePage=true&courseVersionId=8398e23855d44cbdbe60109e52e54121&activityId=0a69188c2cf711ea85dd005056b13345&type=1&isStuLearningRecord=2&token="]
'''
/@CourseHeartUrlList中存放的是个人Heart链接列表,自行替换
OneUrl = CourseHeartUrlList[0]
TwoUrl = CourseHeartUrlList[1]
UserCookieDict = {"Cookie":"JSESSIONID="}
'''
/@UserCookieDict中Cookie:后面是自己的cookie值否则无法正常刷时长,也需要自己获取替换
'''
'''
/@下面开始进行网课刷课
'''
while 1:
try:
time.sleep(1)
ResponeText1 = requests.get(OneUrl,headers=UserCookieDict).json()
ResponeCode1 = ResponeText1["code"]
ResponeText1 = requests.get(TwoUrl,headers=UserCookieDict).json()
ResponeCode2 = ResponeText1["code"]
if ResponeCode1 != "SUCCESS" or ResponeCode2 != "SUCCESS":
print(u"Cookie可能过期,请及时更新")
break
else:
print(u"执行无异常,正在执行中...")
except:
continue
'''
/@使用捕获异常防止发生假性网络中断导致脚本异常结束
'''
`
**[上面的代码是我后来完善的结果,在测试中发现只需要携带cookie的jsessionid就可以了,过期重新获取一次就行],测试了能有一小时,每次时间都在变化,有次能证明这个就是提交给服务器进行在线时长统计的接口了。
现在回到上面说过的“点进去=学过”,我分析了一下单门课程页面:
所有的pdf文件类名都是“activity doc”,所有的视频文件类名都是“activity video”
所以我们只要写一条JavaScript语句,获取这些类的数量,进行循环点击就可以了**
var x = document.getElementsByClassName("activity doc")//获取所有activity doc类
var l = x.length//获取其数量
for(var n=0;n<l;n++)
{
x[n].click()//集合中每一项进行点击
}
只要像上面一样在Console控制台中输入就可以进行全部点击了,当然浏览器可能会有一丢丢卡,最后在随便一个标签页右键关闭其他标签刷新一下就能看到结果了,视频也是同理,doc换成video就可以了
登录分析 AND 答案的获取
在写了一段时间之后感觉每次重新获取Cookie好像挺麻烦的,于是就想着,进行模拟登陆,使用session持续连接不就没那么麻烦了吗,也不用登录什么的,还能获取一些基本信息[网课学习时长,有没有新的作业布置什么的],【当然啦,登录只是进行了分析,没有成功实现,技术不到家】
**照例使用调试器进行网络抓包,看看他的登录流程是什么样子的
重新登录了一遍,发现一条带有login字样的URL,看了一下请求,是post请求,携带的参数如上图,有用户名,加密的密码,验证码,还有一个固定的参数,
要是明文还好说,不用分析那么多,现在只能在里面搜搜看有没有UserPassword这个字符串了,因为要从客户端把密码加密,一定要获取输入框里面的内容,所以就一定可以搜得到,去抓js的包,一个文件一个文件的搜搜看,如下图所示,jQuery,layer,这种文件一般不用看了,是些库文件,里面没有我们想要的
最后我在head.js文件中找到了Userpawword:
我们接着往下看
图中开发人员注释的很清楚,首先获取了加密公钥然后把传来的密码进行RSA加密,获取加密公钥的地址是:/user/getRSAPublicKey+当前时间戳,我们看看能不能在调试器中找到这个文件,然后根据他的构造方式来生成一个,和提交过去的密码进行比对,来验证我们的想法
我们在XHR中找到了这个地址,打开看一下
**
我们回到之前的js代码中看一下,有一个data.publicKeyStr,先不管这个是啥意思,我们可以看到在获取公钥那边有一个字符串也是publicKeyStr,所以推测这个就是公钥了,
于是我们准备在控制台中进行验证
虽然我们可以看到密码不是完全一样的【每次公钥都是不同的】但是长得差不多,既然这样就要来试试看模拟登陆了**
**我们还要一个验证码,来看看验证码的接口:
上面就是验证码的接口,可是python里面怎么用RSA呢,我也不会。。。百度了一份别人写好的,自己改了改**
#--* coding:utf-8 --*
import requests,time,base64,subprocess
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5 as pkc#不知道为什么这么写
#安装的模块: pycryptodome
def GetRsaKey(Password="285912"):
SchoolCourseNetHost = "jsou.cn/"
RsaPublicUrl = SchoolCourseNetHost + "jxpt-web/user/getRSAPublicKey?date=" + str(int(time.time()))
RsaJsonInfo = requests.get(RsaPublicUrl).json()
PublicKey = "-----BEGIN PUBLIC KEY-----\n" + RsaJsonInfo["publicKeystr"] + "\n-----END PUBLIC KEY-----"
RsaKey = RSA.importKey(PublicKey)
RsaEn = pkc.new(RsaKey)
Raw_Password = Password
EnPassword = base64.b64encode(RsaEn.encrypt(Raw_Password))
return EnPassword
def VerCode():
VerCodeUrl = "pt-web/user/getImage?type=1"
CodePicByte = requests.get(VerCodeUrl).content
with open("ver.jpg","wb") as f:
f.write(CodePicByte)
subprocess.Popen("ver.jpg",shell=True)
vercodetext = raw_input("请输入看到的验证码:").replace(" ","")
return vercodetext
def loginMain():
login_url = "http://web/user/login"
#h = {"Content-Type":"application/x-www-form-urlencoded; charset=UTF-8"}
da = {"userName":"202","userPassword":GetRsaKey(),"userLoginPicCode":VerCode(),"checkpic":1}
print(requests.post(login_url,data=da))
#print(type(da))
loginMain()
[结果就是上面的代码得到cookie是没有用的,验证码识别做起来麻烦,而且这也就是个小demo就直接肉眼识别好了],所以这个是放弃了,登录分析到这里也就结束了
接下来说一下答案的获取,答案的获取我也是一次无聊看了一下作业的源代码发现里面有答案的
首先来到作业的页面看一下源代码:
我们可以在开头不远处看到一大串JavaScript代码,而且里面有很多中文:
仔细观察发现还和题目有点像,可以肯定就是答案了,接着我们把她拿去格式化
然后拿回本地看一下
看到一个correAnswer,我对这个词比较敏感,以前在kfc上班的时候考试,在家就抓包找答案,答案字样也是这个单词,所以就可以肯定这个就是答案啦
满分截图:
突破签名限制 AND 补交作业
这个是我今天刚发现的,根据以上协议我做了个刷网课的工具,从最初的1.0(py)已经更新到了4.0(exe),所以在签名里面给个地址给大家下载,结果就给我提示这个:
于是我就想突破这个签名限制,于是迅速定位到签名标签
判断长度什么的应该是由JavaScript来控制完成的,所以在里面搜索这个id名称
从上图我们可以看到,这里有一条判断个性签名的长度,超过20他就弹出提示。
最开始我是想在这个页面里面直接修改逻辑然后让他执行,然而结果就是:不可以,因为这个页面已经渲染完成了,怎么改都没用。
那就用Fidder来改,首先拦截全部,然后再刷新一次页面:
**点击刚才那个页面,选择b黄色的按钮,拦截返回,在下面修改页面的逻辑,改成150【因为多次试验过150字符是服务器接收的,否则返回401】,然后点击绿色按钮运行
取消拦截,把之前请求全部释放
然后再去看一下页面的逻辑,已经改成150了,这时候修改签名就没限制了,这里就不演示了。
既然签名也是要上传服务器然后到前台展示的,一定也是有接口,所以又看了看那段JavaScript代码,发现如下地址:
图中使用post请求向目标地址携带signatures参数来修改签名,试了几次也是可以的
接着来说一下补交作业,因为今天是18号了,我有一个作业15号就要交,但是我忘记去交了,但是去看了一眼,已经没有回复的按钮了
转念一想,这玩意也是提交服务器啊,不知道有没有被关,试试看吧,于是找到一个还可以回复的帖子,抓个包,抓到如下结果:
发现和我刚才提交的一样,所以这个就是讨论的接口地址了,再来看看他的get参数---
本来还想在代码里面找,看了一眼url,这个在url里面就是有的---
转到postman来把不能回复的帖子按照这个构造一次发个包看看---
成功了---
结语
到这里我的分析就结束了,其实7点多就应该完成发表的,写完了,忘记保存,拖动图片的时候不小心页面直接没了,真心想哭,哎,我也是把这段时间上网课做的事情分享出来,以后再看的时候也是别有一番风味,还请各位大佬多多指点哇,嘿嘿
更新
刚才发现了学校提交评论的漏洞,文本允许提交HTML的p标签,在p标签内嵌套script标签,浏览器自动闭合,可以实现XSS攻击。
|