浅谈Python在CTF中的运用

0x00 前言

现在大部分的程序猿对网络安全这个领域几乎一点都不了解,不要问为什么,我接触过很多程序猿(有不少是大厂优秀的程序猿,甚至没有一点安全意识,对网络安全这个领域也没什么具体或者抽象的概念),先来科普一下CTF,CTF在百度百科里的官方释义如下:

CTF(Capture The Flag)中文一般译作夺旗赛,在网络安全领域中指的是网络安全技术人员之间进行技术竞技的一种比赛形式。CTF起源于1996年DEFCON全球黑客大会,以代替之前黑客们通过互相发起真实攻击进行技术比拼的方式。发展至今,已经成为全球范围网络安全圈流行的竞赛形式,2013年全球举办了超过五十场国际性CTF赛事。而DEFCON作为CTF赛制的发源地,DEFCON CTF也成为了目前全球最高技术水平和影响力的CTF竞赛,类似于CTF赛场中的“世界杯”。

CTF是一种流行的信息安全竞赛形式,其英文名可直译为“夺得Flag”,也可意译为“夺旗赛”。其大致流程是,参赛团队之间通过进行攻防对抗、程序分析等形式,率先从主办方给出的比赛环境中得到一串具有一定格式的字符串或其他内容,并将其提交给主办方,从而夺得分数。为了方便称呼,我们把这样的内容称之为“Flag”。

CTF的赛题主要由WEB,MISC,REVERSE,PWN,CRYPTO组成,现在不少比赛还增加了BLOCKCHAINS的题。所需要的知识包括但不限于WEB渗透、数字取证、隐写分析、流量分析、逆向、密码、漏洞挖掘与运用、安全编程等。

我是从去年九月才入坑CTF的,那时候是为了准备首届浙江省的CTF省赛,拉了个队伍运气好拿了三等奖,当然现在比那时候强一点,目前我们战队最好成绩是NJUPT CTF2018打进前十,我在队伍里主打WEB和MISC,也可以搞搞CRYPTO,但是PWN和REVERSE只有入门级水平了…

0x01 正文

基本情况介绍完了,下面开始进入正题,Python在CTF中的运用,首先Python是轻量级的脚本语言,并且有非常非常多的库,import一下,直接调用就完事了,非常方便,首先要讲的就是通过Python脚本的编解码,这也是CTF比赛常用脚本中运用较多的功能,以前没会Python的时候什么编码解码全要找在线工具,听说一听说省赛的决赛是线下赛并且处于断网环境下(主要是为了防止代打),就开始恶补Python,一开始接触的就是编码解码脚本,CTF比赛中最常用的编码应该就是base64了,当时印象非常深刻,就是Python的简洁强大,以前用的比较熟练的是C++,自从那时候开始准备比赛需要用到本地脚本来处理的时候,一开始Base64编解码图片用的是C++,代码差不多就是下面这一长篇,通过右侧代码缩略图预览就知道有多少了

Python要多少呢?后来写出来以后发现只要几行。

想了想C++写的比较多的原因主要是参考了别人的一个项目,有一个过滤不合法字符的过程,另外就是需要自己写一个Base64.h来定义base64的类。后来又从大佬那里搞了一个解码脚本,大佬是很早以前写的,那时候还是Python2写的,因为当时能力有限,一直在用大佬的脚本,现在这个脚本已经被我用Python3改写了,同时正在完善一些新的功能。先上大佬的脚本:

#-*-coding:utf-8-*-
#author:hell0_w
#本人博客:http://hell0w.cnblogs.com/

import base64
import bubblepy
import urllib
import quopri
import cgi
import HTMLParser
import requests

#加密函数:
def base16_encode(content):
 return base64.b16encode(content)
def base32_encode(content):
 return base64.b32encode(content)
def base64_encode(content):
 return base64.b64encode(content)
def BubbleBabble_encode(content):
 return bubblepy.BubbleBabble().encode(content)
def url_encode(content):
 return urllib.quote(content)
def dec_binary(content):
 list = []
 for i in content.split(' '):
 list.append(bin(int(i)).replace('0b',''))
 return list
def str_binary(content):
 list = []
 for i in content:
 list.append(ord(i))
 list1 = []
 for j in list:
 list1.append(bin(int(j)).replace('0b',''))
 return list1
def quoted_printable_encode(content):
 return quopri.encodestring(content)
def HtmlParser_encode(content):
 return cgi.escape(content)


#解密函数:
def base16_decode(content):
 return base64.b16decode(content)
def base32_decode(content):
 return base64.b32decode(content)
def base64_decode(content):
 return base64.b64decode(content)
def BubbleBabble_decode(content):
 return bubblepy.BubbleBabble().decode(content)
def url_decode(content):
 return urllib.unquote(content)
def binary_dec(content):
 list = []
 for i in content.split(' '):
 list.append(int(i,2))
 return list
def binary_str(content):
 list = []
 for i in content.split(' '):
 list.append(int(i,2))
 list1 =[]
 for j in list:
 list1.append(chr(j))
 return ''.join(list1)
def quoted_printable_decode(content):
 return quopri.decodestring(content)
def HtmlParser_decode(content):
 return HTMLParser.HTMLParser().unescape(content)


def encode():
 print "[1]:base16编码"
 print "[2]:base32编码"
 print "[3]:base64编码"
 print "[4]:BubbleBabble编码"
 print "[5]:url编码"
 print "[6]:十进制转二进制"
 print "[7]:字符串转二进制"
 print "[8]:quoted-printable编码"
 print "[9]:HTML实体编码"
 operation = input("请选择:")
 strs = raw_input("请输入需要加密的字符串:")
 if operation == 1:
 try:
 print "[+]加密的结果为:%s " % base16_encode(strs)
 except Exception,e:
 print e

 elif operation == 2:
 try:
  print "[+]加密的结果为:%s " % base32_encode(strs)
 except Exception,e:
 print e

 elif operation == 3:
 try:
 print "[+]加密的结果为:%s " % base64_encode(strs)
 except Exception,e:
 print e

 elif operation == 4:
 try:
 print "[+]加密的结果为:%s " % BubbleBabble_encode(strs)
 except Exception,e:
 print e

 elif operation == 5:
 try:
 print "[+]加密的结果为:%s " % url_encode(strs)
 except Exception,e:
 print e

 elif operation == 6:
 try:
 print "[+]加密的结果为:%s " % dec_binary(strs)
 except Exception,e:
 print e

 elif operation == 7:
 try:
 print "[+]加密的结果为:%s " % str_binary(strs)
 except Exception,e:
 print e

 elif operation == 8:
 try:
 print "[+]加密的结果为:%s " % quoted_printable_encode(strs)
 except Exception,e:
 print e

 elif operation == 9:
 try:
  print "[+]加密的结果为:%s " % HtmlParser_encode(strs)
 except Exception,e:
 print e
 else:
 print "error!"
 encode()


def decode():
 print "[1]:base16解码"
 print "[2]:base32解码"
 print "[3]:base64解码"
 print "[4]:BubbleBabble解码"
 print "[5]:url解码"
 print "[6]:二进制转十进制"
 print "[7]:二进制转字符串"
 print "[8]:quoted-printable解码"
 print "[9]:HTML实体解码"
 operation = input("请选择:")
 strs = raw_input("请输入需要解密的字符串:")
 if operation == 1:
  try:
 print "[+]解密的结果为:%s " % base16_decode(strs)
 except Exception,e:
 print "Error!Not base16 encoding!"

 elif operation == 2:
 try:
 print "[+]解密的结果为:%s " % base32_decode(strs)
 except Exception,e:
 print "Error!Not base32 encoding!"

 elif operation == 3:
 try:
 print "[+]解密的结果为:%s " % base64_decode(strs)
 except Exception,e:
 print "Error!Not base64 encoding!"

 elif operation == 4:
 try:
 print "[+]解密的结果为:%s " % BubbleBabble_decode(strs)
 except Exception,e:
 print "Error!Not BubbleBabble encoding!"

 elif operation == 5:
 try:
 print "[+]解密的结果为:%s " % url_decode(strs)
  except Exception,e:
 print "Error!Not url encoding!"

 elif operation == 6:
 try:
 print "[+]解密的结果为:%s " % binary_dec(strs)
 except Exception,e:
 print "Error!Not binary encoding!"

 elif operation == 7:
 try:
 print "[+]解密的结果为:%s " % binary_str(strs)
 except Exception,e:
 print "Error!Not binary encoding!"

 elif operation == 8:
 try:
 print "[+]解密的结果为:%s " % quoted_printable_decode(strs)
 except Exception,e:
 print "Error!Not quoted-printable encoding!"

 elif operation == 9:
 try:
 print "[+]解密的结果为:%s " % HtmlParser_decode(strs)
  except Exception,e:
 print "Error!Not HtmlParser encoding!"
 else:
 print "error!"
 decode()


def main():
 print "[1]:加密"
 print "[2]:解密"
 operation = input("请选择:")
 if operation == 1:
 encode()
 elif operation == 2:
 decode()
 else:
 print "error!"
 main()

if __name__ == '__main__':
 main()

不得不说这个脚本当时帮了很大的忙,现在自己改的这个多了很多其他的进制转换,还有一些别的编码解码。这类简单的编解码主要在MISC里出现,找到了被加密的flag直接拿来解个码就拿到分了,base64当然平时并不是这么用的,base64编码主要用于在HTTP传输时用来加密信息,例如,在Java Persistence系统Hibernate中,就采用了Base64来将一个较长的唯一标识符(一般为128-bit的UUID)编码为一个字符串,用作HTTP表单和HTTP GET URL中的参数。在其他应用程序中,也常常需要把二进制数据编码为适合放在URL(包括隐藏表单域)中的形式。此时,采用Base64编码具有不可读性,需要解码后才能阅读。虽然这是一种非常简单的加密方式,但是没有点网络基础的还真不容易看懂,就如简单的admin这个字符串经过base64加密后变成了YWRtaW4=,大多数人应该都看的一脸懵逼。既然用在网络传输数据上,那么CTF中的WEB题也必然会遇到类似的问题,当然需要用到base64编解码,我们拿一个题来作为例子,地址:http://123.206.87.240:8002/web6/这是bugku上一个非常经典的WEB题,需要用到Python脚本来解决,其中base64只是基础,我们还需要用到requests包来POST信息。首先打开网页,你会发现显示的是你需要在快点,在通过抓包分析以后发现服务器返回信息里直接存在flag但是并不是答案,

因为在网页中还有一句注释需要你把得到的flag解码后作为margin然后POST给服务器他才会返回最终答案,

开始是尝试了把字符串拉下来用脚本解码后再发送出去服务器还是觉得我慢(掀桌!)于是尝试用Python写脚本来POST数据,首先解码以后的flag并不全是我需要的,拿上图的flag来举例,第一次base64解码以后是“跑的还不错,给你flag吧: MzYwMzAx”我们需要把MzYwMzAx这个字符串再作为新的请求包里的margin参数给POST回去,这个时候就需要用到split函数来分割字符串,我们只需要冒号以后的字符串,于是写了如下脚本,运行以后就得到了flag。

import requests
import base64

s =requests.Session()
headers =s.get("http://123.206.87.240:8002/web6/").headers
str1 = base64.b64decode(headers['flag'])
str2 = base64.b64decode(repr(str1).split(':')[1])
data= {'margin':str2}
flag = s.post("http://123.206.87.240:8002/web6/",data=data)
print(flag.text)

另外Python还可以写一些简单密码的解密比如凯撒密码,短短数十行就可以实现

基于Python在大数据方面出色的处理性能,还可以用于复杂密码的加密解密比如RSA这种常见的加密算法,我们在NCTF中遇到的题

对于这样大的数字,别的语言早就超过定义范围不知道多少倍了,但是Python可以很方便的处理,这个题大致思路就是先利用pq=n p-q=140484…列出一元二次方程,用BigIntergerCal计算出pq,再用RSAtool计算出d,算出d再用BigIntergerCal解出明文

m=45411192849370852342508097771685415309715836886014345394100707709

再用BigIntergerCal转为十六进制转为字符得到flag,脚本找不到了,只有当初写WP的截图了…

除了大数据的处理,Python在图像方面也是非常有优势,因为非常多的数据处理到头来都需要实现可视化,丰富的库提供了多样化的各种图表类型,之前遇到过的一个MISC类型的题,给了一份具有长达六万多行的数字的文本文件

初步怀疑是RGB颜色,遂通过以下脚本实现可视化,得到答案

from PIL import Image
import re

x = 503 #x坐标 通过对txt里的行数进行整数分解
y = 122 #y坐标 x*y = 行数

im = Image.new("RGB",(x,y))#创建图片
file = open('misc80.txt') #打开rbg值文件

#通过一个个rgb点生成图片
for i in range(0,x):
 for j in range(0,y):
 line = file.readline()#获取一行
 rgb = line.split(",")#分离rgb
 im.putpixel((i,j),(int(rgb[0]),int(rgb[1]),int(rgb[2])))#rgb转化为像素
im.show()

还有一份又是几万行的数据,这次明显是坐标,通过Python数据分析中常用的图表将图像还原以后得到一个藏有flag的二维码。

脚本如下,简简单单十几行处理了万条数据。

import matplotlib.pyplot as plt
import numpy as np
import matplotlib as mpl

mpl.rcParams['font.family'] = 'sans-serif'
mpl.rcParams['font.sans-serif'] = 'NSimSun,Times New Roman'

x, y = np.loadtxt('1.txt', delimiter=',', unpack=True)
plt.plot(x, y, '.', label='Data', color='black')

plt.xlabel('x')
plt.ylabel('y')
plt.title('Data')
plt.legend()
plt.show()

当然还有很多别的处理图像的运用,这个脚本内容比较多就不贴了,来自大佬的github,以下为连接,考查的是盲水印 https://github.com/chishaxie/BlindWaterMark 盲水印科普,就是看起来两个一样的图,,实际上是一张原图里插入了肉眼不可见的另一张图(也就是要插入的水印),可以通过脚本的各种操作来还原

由于CTF中遇到需要Python脚本来解决的东西实在太多,加上本人才疏学浅(其实就是太菜了,文化人讲话要文绉绉的)无法将所有的类型都详细介绍,就靠以上几个简单的例子大致介绍了几种类型,以下再贴一个大佬用的实现各种操作的各种脚本,来向你们说明其实脚本还可以这么玩

0x02 福利

当然除了CTF,网络安全领域也有用到其他非常多的Python编写的工具,2017最受欢迎的几个工具,都是用Python编写的,有兴趣的大家可以去看一看。

一、p0wnedShell – PowerShell Runspace 漏洞利用后期工具包

地址:https://github.com/Cn33liz/p0wnedShell

二、mongoaudit – MongoDB 审计与渗透测试工具 Tool

地址:https://github.com/stampery/mongoaudit

三、spectrology – 可在声音文件中隐藏图片的音频密写工具

地址:https://github.com/solusipse/spectrology 这里只是一部分,更多Python开源工具请访问:https://github.com/dloss/python-pentest-tools

0x03 写在最后

我很菜,没有公众号,没有博客(以前的域名到期了就没申请,就不写博客了,反正我这么菜,写了也没人看),在此不做推广。知乎玩的也比较少,如果有什么建议意见之类的私信就好了,如果各位师傅觉得我文章写的太烂了之类的,希望别喷就好。

本文分享自微信公众号 - 安恒网络空间安全讲武堂(cyberslab)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-02-19

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券