网络安全中的python_一文掌握CTF中Python全部考点

前 言

一次偶然的机会,让自己成为了一名CTF夺旗小白。从16年开始参与国内大中小型CTF竞赛,曾记得17年之前很少有人把这类竞赛叫做CTF,都是叫网络攻防赛、信息安全赛之类的,目的就是为了通过各种技术手段找到题目最终的key,现在我们都称之为Flag。

做为CTF小白用户,这四年跳过的坑真心不少,从最初的“实验吧”等免费的线上模拟平台到省级网络安全技能大赛,再到国内知名的XCTF联赛、DEFCON CTF,基本是跳一个栽一个。回首CTF的发展历程,也从最基本的解题模式发展成为如今的解题模式(Jeopardy)、攻防模式(Attack-Defense)、混合模式(Mix)三者于一体的网络安全竞赛,涵盖题型仍然是MISC、PPC、CRYPTO、PWN、REVERSE、WEB、STEGA等。

从题目的难易程度来看,题目难度不断加大,无论是MISC、CRYPTO、PWN、REVERSE还是WEB,都不再是那种用工具打开随便看看就能得到flag的题目了,几乎每个题目都会涉及到编写脚本来解题,而人生苦短我相信绝大多数人还是会选择Python。

从题型分布来看,PWN和REVERSE题目是每场CTF比赛的重头戏,也就是说只要PWN和REVERSE做的好,不进线下赛都困难~~,而Web题型也从原始的PHP考点向Python转型,从2018年开始,陆续出现了很多Python题目,如:18年护网杯的easy_tonado、强网杯的Python is the best language、TWCTF的Shrine、19年的SCTF也出了Ruby ERB SSTI的考点。

因此,本文就CTF中常见的Python考点进行了学习与汇总,在此也感谢各种大佬在自己博客中的辛勤付出,由于CTF题目在复现过程中出现了各种各样的问题,某些地方也直接照搬了大佬的博客。

一、CTF-pyc考点

1.1 pyc文件简介

pyc文件是py文件编译后生成的字节码文件(byte code),pyc文件经过python解释器最终会生成机器码运行。因此pyc文件是可以跨平台部署的,类似Java的.class文件,一般py文件改变后,都会重新生成pyc文件。

1.2 pyc文件生成方式

方法一:

在同一目录下创建两个py文件:test1.py 和 test2.py

test1.py文件内容:def a():

return 1

def b():

return 2

x = a()

y = b()

print(x + y)

test2.py文件内容:import test1

运行python test2.py生成test1.pyc文件。

方法二:python -m test1.py

直接运行以上命令也可以生成.pyc文件

方法三:

借助py_compile库和py_compile.compile('foo.py')方法

如果针对一个目录下所有的py文件进行编译,Python提供了一个模块叫compileall,具体请看下面代码:import compileall

compileall.compile_dir(r'/path')

1.3 pyc文件结构

接下来我们简单分析一下 pyc 文件结构,使用010 editor打开.pyc文件。

pyc文件一般由3个部分组成:最开始4个字节是一个Maigc int, 标识此pyc的版本信息, 不同的版本的Magic都在Python/import.c内定义

接下来四个字节还是个int,是pyc产生的时间(TIMESTAMP, 1970.01.01到产生pyc时候的秒数)

接下来是个序列化了的 PyCodeObject(此结构在Include/code.h内定义),序列化方法在Python/marshal.c内定义

1.4 CTF案例:PYTHON 字节码

python逆向基础资源:

crackme.pyc反编译后的内容如下:#!/usr/bin/env python

# encoding: utf-8

# 如果觉得不错,可以推荐给你的朋友!http://tool.lu/pyc

def encrypt(key, seed, string):

rst = []

for v in string:

rst.append((ord(v) + seed ^ ord(key[seed])) % 255)

seed = (seed + 1) % len(key)

return rst

if __name__ == '__main__':

print "Welcome to idf's python crackme"

flag = input('Enter the Flag: ')

KEY1 = 'Maybe you are good at decryptint Byte Code, have a try!'

KEY2 = [

124,

48,

52,

59,

164,

50,

37,

62,

67,

52,

48,

6,

1,

122,

3,

22,

72,

1,

1,

14,

46,

27,

232]

en_out = encrypt(KEY1, 5, flag)

if KEY2 == en_out:

print 'You Win'

else:

print 'Try Again !'

def encrypt(key, seed, string):

rst = []

for v in string:

rst.append((ord(v) + seed ^ ord(key[seed])) % 255)

seed = (seed + 1) % len(key)

return rst

if __name__ == '__main__':

print "Welcome to idf's python crackme"

flag = input('Enter the Flag: ')

KEY1 = 'Maybe you are good at decryptint Byte Code, have a try!'

KEY2 = [

124,

48,

52,

59,

164,

50,

37,

62,

67,

52,

48,

6,

1,

122,

3,

22,

72,

1,

1,

14,

46,

27,

232]

en_out = encrypt(KEY1, 5, flag)

if KEY2 == en_out:

print 'You Win'

else:

print 'Try Again !'

从程序看,KEY2内的整数似乎像ascii数值,但数字和英文字符少,直接转换意义不大。关键在于分析encrypt(KEY1, 5, flag)。

对encrypt函数的分析:用户输入一个字符串(ascii值必小于128),然后取出每个字符求其ascii值,加上seed,然后用其和与KEY1中一字符的ascii进行异或(算符\^,注意+比^的优先级高),然后对255求余。

编写解密程序。显然正确的密码字符串加密后结果为KEY2,那么逆向分析编码即可。程序如下:#python script

KEY2 = [124,

48,

52,

59,

164,

50,

37,

62,

67,

52,

48,

6,

1,

122,

3,

22,

72,

1,

1,

14,

46,

27,

232]

KEY1 = 'Maybe you are good at decryptint Byte Code, have a try!'

def encrypt(key, seed, string):

rst = []

for v in string:

rst.append((ord(v) + seed ^ ord(key[seed])) % 255)

seed = (seed + 1) % len(key)

return rst

def decrypt(key,seed,en_out ):

string = ''

for i in en_out :

v = (i ^ ord(key[seed]))-seed

seed = (seed + 1) % len(key)

if v > 0:

string += chr(v)

return string

if __name__ == '__main__':

print decrypt(KEY1,5,KEY2)

得到flag:WCTF{ILOVEPYTHONSOMUCH}

二、Python序列化和反序列化

2.1 序列化:把一个类对象转化为字节流(1)从对象提取所有属性,并将属性转化为名值对

(2)写入对象的类名

(3)写入名值对

在python中,一般可以使用pickle类来进行python对象的序列化,而cPickle提供了一个更快速简单的接口。

cPickle可以对任意一种类型的python对象进行序列化操作,比如list,dict,甚至是一个类的对象等。而所谓的序列化,可理解就是为了能够完整的保存并能够完全可逆的恢复。

1、dump: 将python对象序列化保存到本地的文件import cPickle

data = range(1000)

cPickle.dump(data,open("test\\data.pkl","wb"))

dump函数需要指定两个参数,第一个是需要序列化的python对象名称,第二个是本地的文件,需要注意的是,在这里需要使用open函数打开一个文件,并指定“写”操作

2、load:载入本地文件,恢复python对象data = cPickle.load(open("test\\data.pkl","rb"))

3、dumps:将python对象序列化保存到一个字符串变量中data_string = cPickle.dumps(data)

2.2 反序列化:将字节流转化为原始对象

(1)获取 pickle 输入流

(2)重建属性列表

(3)根据类名创建一个新的对象

(4)将属性复制到新的对象中

1、load:载入本地文件,恢复python对象data = cPickle.load(open("test\\data.pkl","rb"))

2、loads:从字符串变量中载入python对象data = cPickle.loads(data_string)

Python 的序列化的目的是为了保存、传递和恢复对象的方便性,在众多传递对象的方式中,序列化和反序列化可以说是最简单和最容易的方式。

Python序列化实例#!/usr/bin/env python

# encoding: utf-8

import os

import pickle

class test(object):

def __reduce__(self):

return (os.system,('ls',))

a=test()

payload=pickle.dumps(a)

print payload

pickle.loads(payload)

对应的格式如下:c:读取新的一行作为模块名module,读取下一行作为对象名object,然后将module.object压入到堆栈中。

(:将一个标记对象插入到堆栈中。为了实现我们的目的,该指令会与t搭配使用,以产生一个元组。

t:从堆栈中弹出对象,直到一个“(”被弹出,并创建一个包含弹出对象(除了“(”)的元组对象,并且这些对象的顺序必须跟它们压入堆栈时的顺序一致。然后,该元组被压入到堆栈中。

S:读取引号中的字符串直到换行符处,然后将它压入堆栈。

R:将一个元组和一个可调用对象弹出堆栈,然后以该元组作为参数调用该可调用的对象,最后将结果压入到堆栈中。

.:结束pickle。

2.3 Python反序列化漏洞

成因:当传入了不安全的反序列化函数的内容,就会产生反序列化漏洞,造成任意代码执行。

通常出现在解析认证token,session的时,flask配合redis在服务端存储session的情景。

这里的session是被pickle序列化进行存储的,如果你通过cookie进行请求session-id的话,session中的内容就会被反序列化,看似好像是没有什么问题。因为session是存储在服务端的,但是终究是抵不住redis的未授权访问,如果出现未授权的话,我们就能通过set设置自己的session,然后通过设置cookie去请求session的过程中我们自定的内容就会被反序列化,然后我们就达到了执行任意命令或者任意代码的目的。

2.4 Python unpickle 造成任意命令执行漏洞

web端源码:import pickle

import base6

  • 5
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值