前几天看到了一则新闻,某校园一名学生因进入教育系统擅自修改自己几门已经挂科的成绩而被开除,当然我就觉得这位大哥比较牛逼,毕竟我入侵过多次,也不该擅自修改自己的成绩,这确实是已经触犯了学校规则的底线,开始就是觉得他挺厉害的,能入侵,还能修改自己的成绩,但是回头一想,如果他真有这水平。就算不是成绩很好的学霸,也不应该是挂科六门的学渣了。
我是比较欣赏这样的大哥的,但是以为是个王者,别想到确实个青铜。根据知情人士的透露,原来他是懵对了老师的密码。果然是个青铜!
发一段我在新闻上看到的消息吧。目前,该校已经更新了中央认证系统,并升级了账号安全措施。
当然入门Python没有好的学习资料怎么行呢?所以小编这里准备了一份Python学习资料,添加小编学习交流群943752371即可
该校工作人员表示,在整理成绩时发现,该生的成绩在没有任何理由的情况下发生了变动。并且是门门成绩同时发生了变动。而一般来说,修改成绩都需要任课教师向教务提起申请然后教务再对试卷进行审核才会对成绩进行修改,但当学校询问任教教师,老师都表示没有向教务提起修改成绩的要求。
后来才知道原来是懵对了老师的密码!老师也是粗心,没想到就是123456,不过也是,正常人一般都不会敢擅自修改自己的成绩的。毕竟每个同学都是老师所给的分。通过平常的考核,上课的考勤,以及学期末的考试来综合给定分数的,如果一位老师对你没有什么印象。他也不会给你挂科的。肯定是经常逃课,或者之类的,老师印象比较深刻,所以你改成绩也是没用的。当然小编来入侵教务系统呢就是弄着玩的。像小编这样品学兼优的三好学生,怎么可能会挂科嘛。嘿嘿。自卖自夸!好了。废话了这么多。来到咱们的正文!
当你修改成绩的时候,它会产生一条fatal级别的日志,以后用作生成报表,供教务系统人员计算绩效之类的用途。
改得了教务系统,改不了记住了你的人。
修改成绩被抓,轻则记大过永远留档,重则被踢出校并且留下严重诚信问题。
与其花时间去剑走偏锋,不如
尝试登录
首先我们打开学校的教务系统,随便输入,然后提交表单,打开Chrome的开发者工具中的Network准备抓包
把css 图片之类的过滤掉,发现了default.aspx这个东西
如果你们学校教务系统不使用Cookie则会是这样
服务器会返回一个Cookie值,然后在本地保存,这与下面的会不相同。
获取会话信息(不使用Cookie)
这里我们要使用requests库,并且要伪造header的UA信息
上面获取的url即为带有会话信息的网址,保存的url格式为http://110.65.10.xxx/(bdq1aj45lpd42o55vqpfgpie)/
保存为这样的格式是因为我们要访问其他地址
获取会话信息(使用Cookie)
有些学校的教务系统是使用Cookie的,我们只需要首次get请求时保存Cookie即可,然后此后一直使用该cookie
def get_cookie():
request = requests.get('http://xxx.xxx.xxx.xxx') #以某教务系统为例子
cookie = requets.cookie
return cookie
而requests中使用Cookie很简单
只需要这样
def use_cookie(cookie):
request = requests.get('http://xxx.xxx.xxx.xxx',cookie=cookie)
由于我们学校采用的是无Cookie方案,所以下面的代码均没有发送Cookie,如果你的学校采用了Cookie,只需要像我上面这样发送Cookie就行了。
而如果你们学校使用Cookie,就不必获取带有会话信息的地址了,直接存储Cookie即可。
验证码的处理
分析r返回的文本信息
发现验证码的标签的资源地址为 src=”CheckCode.aspx” ,我们可以直接requests然后下载验证码图片,下载图片的一种优雅的方式如下
发现有信息无法被解码,应该是gb2312编码,查看解码前的编码
然后将不能解码的代码复制能够解码的地方
发现%D1%A7%C9%FA编码解码后为学生
这也就对应了学生选项的登录
学号和密码和验证码能够显而易见地知道是哪些信息,但是我们发现有__VIEWSTATE这一项
查找一下,这是一个表单隐藏信息,我们可以用BeautifulSoup库解析可以得出该一项数据的值这是完整的登录数据包,
登录
如果登录完成了,如何判断是否登录成功呢?我们从登录成功返回的界面发现有姓名这一标签,而我们等一下也是需要学生姓名,所以我们用这个根据来判断是否登录成功。
代码如下,进行了验证码用户名和密码的提示信息判别
def login(self,uid,password):
whileTrue:
data = self.__get_login_data(uid,password)
request = requests.post(self.__real_base_url +'default2.aspx', headers=self.__headers, data=data)
soup = BeautifulSoup(request.text,'lxml')
try:
name_tag = soup.find(id='xhxm')
self.__name = name_tag.string[:len(name_tag.string) - 2]
print('欢迎'+self.__name)
except:
print('Unknown Error,try to login again.')
time.sleep(0.5)
continue
finally:
return True
获取选课信息
接下来就是获取选课信息了,这里我们以校公选课为例子,点击进去,进行抓包,headers没有什么好注意的,我们只用关注get发送的包即可
发现有学号与姓名与gnmkdm这一项,姓名我们需要编码为gb2312的形式才能进行传送
这里我们注意headers需要新增Referer项也就是当前访问的网址,才能进行请求
def __enter_lessons_first(self):
data = {
'xh': self.__uid,
'xm': self.__name.encode('gb2312'),
'gnmkdm': 'N121103',
}
self.__headers['Referer'] = self.__real_base_url + 'xs_main.aspx?xh=' + self.__uid
request = requests.get(self.__real_base_url +'xf_xsqxxxk.aspx', params=data, headers=self.__headers)
self.__headers['Referer'] = request.url
soup = BeautifulSoup(request.text,'lxml')
self.__set__VIEWSTATE(soup)
注意到上面有一个设置VIEWSTATE值的函数,这里等下在选课构造数据包的时候会讲
模拟选课
随便选一门课,然后提交,抓包,看一下有什么数据发送
然后我们关注一下这条数据,我们搜索一下,发现这是课程的提交选课的代码,所以我们也可以直接从网页中获取,而on表示选项被选上
kcmcGrid:_ctl2:xk:'on'
搜索课程
课程有很多信息,比如名字,上课时间,地点,这些东西确定好了才知道选的是哪门课,所以我们先新建一个类来存储信息
class Lesson:
def __init__(self,name, code, teacher_name, Time, number):
self.name = name
self.code = code
self.teacher_name = teacher_name
self.time = Time
self.number = number
def show(self):
print('name:' + self.name + 'code:' + self.code + 'teacher_name:' + self.teacher_name + 'time:' + self.time)
有了这个类,我们就可以进行搜索课程了,具体代码看下面代码,解析网页内容就不细讲了。
def __search_lessons(self, lesson_name=''):
self.__base_data['TextBox1'] = lesson_name.encode('gb2312')
request = requests.post(self.__headers['Referer'], data=self.__base_data, headers=self.__headers)
soup = BeautifulSoup(request.text,'lxml')
self.__set__VIEWSTATE(soup)
return self.__get_lessons(soup)
def __get_lessons(self, soup):
lesson_list = []
lessons_tag = soup.find('table', id='kcmcGrid')
lesson_tag_list = lessons_tag.find_all('tr')[1:]
for lesson_tag in lesson_tag_list:
td_list = lesson_tag.find_all('td')
code = td_list[0].input['name']
name = td_list[1].string
teacher_name = td_list[3].string
Time = td_list[4]['title']
number = td_list[10].string
lesson = self.Lesson(name, code, teacher_name, Time, number)
lesson_list.append(lesson)
return lesson_list
进行选课
选课我们只要将lesson_list传入即可,这就是我们之前创建的Lesson类的实例的列表,’Button’的内容为’ 提交 ‘,这两边各有一个空格,完事后我们可以进行发送请求进行选课。
这里我们用正则提取了错误信息,比如选课时间未到、上课时间冲突这些错误信息来提示用户,我们还解析了网页的已选课程,这里也不细讲了,都是基础的网页解析。
def __select_lesson(self, lesson_list):
data = copy.deepcopy(self.__base_data)
data['Button1'] = ' 提交 '.encode('gb2312')
for lesson in lesson_list:
code = lesson.code
data[code] ='on'
request = requests.post(self.__headers['Referer'], data=data, headers=self.__headers)
soup = BeautifulSoup(request.text,'lxml')
self.__set__VIEWSTATE(soup)
error_tag = soup.html.head.script
ifnot error_tag is None:
error_tag_text = error_tag.string
r ="alert('(.+?)');"
for s in re.findall(r, error_tag_text):
print(s)
print('已选课程:')
selected_lessons_pre_tag = soup.find('legend', text='已选课程')
selected_lessons_tag = selected_lessons_pre_tag.next_sibling
tr_list = selected_lessons_tag.find_all('tr')[1:]
for tr in tr_list:
td = tr.find('td')
print(td.string)
总结
这次我们完成了模拟正方教务系统选课的过程,由于这个教务系统技术比较陈旧,所以比较好弄,事实上抢课的时候用Fiddler即可完成操作,因为我们只需要提前登录然后记录网址即可。
网友评论