时隔一年,又玩了一次CTF
前言
去年玩了一次,被虐的很惨。本来今年不太想玩,但是又忍不住想试试。 算上这次也总共玩过2次CTF。感觉CTF非常考验基础和综合的能力,抱着玩玩的心理。
Pwn
Tutorial1
提示:学学怎么用pwntools
安装工具,找到文档:http://docs.pwntools.com/en/stable/
IDA打开,发现程序对比输入是否是0xbabababa。
于是
sh.sendline(p32(0xbabababa))即可

Tutorial2
IDA打开,提示WhereToGo,当然是到backdoor啦,于是输入backdoor函数的地址。即可得到flag。
Web
fakelogin
F12发现auth不是很对。
看了一下像base64。
原来flag藏在这里
auth=Tzo0OiJBdXRoIjoyOntzOjg6InVzZXJuYW1lIjtzOjM6InxscyI7czo1OiJsb2dpbiI7aTowO30%3D
base64 解密后即看到flag。
Git
提示很明显。打开 http://35.221.144.41:8085/.git/index 能够得到index文件。
于是使用大佬做的工具解决:https://github.com/lijiejie/GitHack
得到
<?php $flag="flag{g1t_l34kage}"; ?>
realeasy_1
flag 在 html里面,F12打开就能看到。
<body>
<h1>Real Easy Stage 1</h1>
<!-- flag{iSc0mm3ntUsel3ss} -->
</body>realeasy_2
BurpSuite intercept 。
参数后加一位,凑够10,就能看到flag。
flag{TryUrb3st23dithtml}
realeasy_3
提示不能再明显了。
<body><h1>Real Easy Stage 3</h1>
<p>Find the best method to success!</p>
<!-- POST -->
</body>用BurpSuite直接将 Method 改为POST。
weaktype
看到这个就觉得是PHP。
参数有两个:A和B。
根据源码
``if ($A != $B && md5($A) == md5($B)) { echo $flag; }
在网上随便找两个MD5开头为 0e的字符串。即可得到flag。
http://35.221.144.41:8088/?A=QNKCDZO&B=s878926199a
flag{th15f14g_1s_s0weak}
easy web
根据提示php md_seed()函数
找到网址https://www.openwall.com/php_mt_seed/README
在网上搜索到 php_mt_seed cracker的源码。试了几个值。
本来的提示很有用:Flag stored in ./flag/seed.txt 😃
找到flag
http://119.29.191.200:81/flag/1540051390.txt
networktest
首先看了下,好像用了ping。
然后想办法执行命令。参考:https://blog.csdn.net/qq_27446553/article/details/73927518
试了ls,发现目录有index.php。
然后输入 |cat<index.php 看到了index.php的内容。
虽然好像没有什么帮助。发现空格不行,在网上搜绕过空格的方法
输入 $IFS$9 可以代替空格。用ls命令发现其实flag在上级目录。
最终结果:

fakelogin1
网上搜到各种绕过方法,都试了一下。发现加'#'有用。原来#后的字符串都会被当成注释来处理。
所以输入“admin #”成功登陆。登陆之后试了下id,发现是动态的。
然后用sqlmap扫描。
sqlmap.py -r "C:\Users\TOPSECTET\Desktop\fakelogin1" --dbs sqlmap.py -r "C:\Users\Desktop\fakelogin1" -D fakelogin1 -T fffflllaaaaagggggg369 --columns sqlmap.py" -r "C:\Users\TOPSECRET\Desktop\fakelogin1" -D fakelogin1 -T fffflllaaaaagggggg369 -C flagcolcol --dump
得到flag
Crpyto
Toddler RSA
http://factordb.com/index.php?query=35916913381799169598047392904148491391161620899816360736005775761561238969291
用factordb分解N,发现分解出多个素数。到网上搜索,发现RSA支持多个素数(Multi prime RSA)。
计算d的方式也是一样的。
import gmpy2
import codecs
n = 35916913381799169598047392904148491391161620899816360736005775761561238969291
e = 257
c = 8475628327993429564462830126573767419742875741767715368947653567552147606198
# N分解为多个素数
p1 = 10390947166040840011
p2 = 11644261463999014129
p3 = 16998750717539055911
p4 = 17462841690525028999
phin = (p1 - 1) * (p2 - 1) * (p3 - 1) * (p4 - 1)
d = gmpy2.invert(e, phin)
p = gmpy2.powmod(c, d, n)
m = pow(c, d, n)
print(hex(m))
print(codecs.decode(str(hex(m)[2:]), 'hex'))0x666c61677b57656c6c646f6e65796f756b6e6f775253417d b'flag{WelldoneyouknowRSA}'
OverFlowBuf
根据source.py看到是DES加密。对DES了解甚少。直到出现了提示(Sorry,太菜了)。
从加密过程来看,加密使用了key和IV(Initialization Vector) ,然后与plaintext进行XOR来获取ciphertext。
加密只用了两个key,所以通过IV可以计算出一半块的明文。在这里,可以看出是《哈姆雷特》的文本。
现在知道了明文与密文,可以推出key。
内容为“To be, or not to be, that is the question”,可以使用推测出来的明文来计算key。
import string
from Crypto.Cipher import DES
#使用 OFB 模式的DES块加密使用一个未知的key和IV。
#关键点应该是加密重用了keystream,在这种情况下,可以用IV获取偶数块的内容。从而推测原文。https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Output_Feedback_.28OFB.29
def get_blocks(data, block_size):
return [data[i:i + block_size] for i in range(0, len(data), block_size)]
def xor_str(xs, ys):
return "".join(chr(x ^ ord(y)) for x, y in zip(xs, ys))
with open("cipher.txt", "rb") as file:
cipher = file.read()
blocks = get_blocks(cipher, DES.block_size)
IV = 'Le3tc0de'
for b in blocks:
print(b, IV)
x = xor_str(b, IV)
if all(c in string.printable for c in x):
# 全为可打印字符
print(x)
# 内容为“To be, or not to be, that is the question”,可以使用推测出来的明文来计算key。
p = " be, tha"
key = xor_str(blocks[2], p)
s = ""
for i in range(len(blocks)):
if i % 2 == 0:
b = xor_str(blocks[i], key)
else:
b = xor_str(blocks[i], IV)
s += b
print(s)Reverse
Easy_ELF
提示:XOR
于是用IDA打开看看。只是几个判断。
int result; // eax@2
char flag[]="L1NUX";
flag[1] == 49 // 1
flag[0]^=0x34u;
flag[1]^=0x32u;
flag[3]^=0x88u;
if (flag[4]==88)通过这里可以用XOR算到flag其实是L1NUX
ctf-easy
这个是真easy。用工具解包apk。
然后用JD-GUI打开 FlagActivity.class。
里面的代码复制出来直接编译运行,就得到邮箱了。
private static final byte[] p = { -40, -62, 107, 66, -126, 103, -56, 77, 122, -107, -24, -127, 72, -63, -98, 64, -24, -5, -49, -26, 79, -70, -26, -81, 120, 25, 111, -100, -23, -9, 122, -35, 66, -50, -116, 3, -72, 102, -45, -85, 0, 126, -34, 62, 83, -34, 48, -111, 61, -9, -51, 114, 20, 81, -126, -18, 27, -115, -76, -116, -48, -118, -10, -102, -106, 113, -104, 98, -109, 74, 48, 47, -100, -88, 121, 22, -63, -32, -20, -41, -27, -20, -118, 100, -76, 70, -49, -39, -27, -106, -13, -108, 115, -87, -1, -22, -53, 21, -100, 124, -95, -40, 62, -69, 29, 56, -53, 85, -48, 25, 37, -78, 11, -110, -24, -120, -82, 6, -94, -101 };
private static final byte[] q = { -57, -90, 53, -71, -117, 98, 62, 98, 101, -96, 36, 110, 77, -83, -121, 2, -48, 94, -106, -56, -49, -80, -1, 83, 75, 66, -44, 74, 2, -36, -42, -103, 6, -115, -40, 69, -107, 3, -28, -49, 49, 73, -26, 8, 100, -18, 1, -16, 13, -61, -12, 22, 33, 51, -80, -120, 42, -75, -123, -22, -78, -68, -110, -94, -14, 68, -7, 3, -9, 10, 84, 70, -8, -63, 26, 126, -76, -104, -123, -71, -126, -62, -23, 11, -39, 70, 14, 59, -101, -39, -124, 91, -109, 102, -49, 21, 105, 0, 37, -128, -57, 117, 110, -115, -86, 56, 25, -46, -55, 7, -125, 109, 76, 104, -15, 82, -53, 18, -28, -24 };
private String i()
{
int j = 0;
byte[] arrayOfByte1 = new byte[p.length];
for (int i = 0; i < arrayOfByte1.length; i++) {
arrayOfByte1[i] = ((byte)(byte)(p[i] ^ q[i]));
}
int k = arrayOfByte1[0];
for (i = 0; arrayOfByte1[(k + i)] != 0; i++) {}
byte[] arrayOfByte2 = new byte[i];
while (j < i)
{
arrayOfByte2[j] = ((byte)arrayOfByte1[(k + j)]);
j++;
}
return new String(arrayOfByte2);
}div
提示:这是汇编除法优化。
txt的内容非常有用。
我的思路好像有点那个。
$ cat foo.c
long long div(long long x) {
return x / N;
}
$ gcc -DN=$N -c -O2 foo.c
$ objdump -d foo.o
foo.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000
:
0: 48 89 f8 mov %rdi,%rax
3: 48 ba 01 0d 1a 82 9a movabs $0x49ea309a821a0d01,%rdx
a: 30 ea 49
d: 48 c1 ff 3f sar $0x3f,%rdi
11: 48 f7 ea imul %rdx
14: 48 c1 fa 30 sar $0x30,%rdx
18: 48 89 d0 mov %rdx,%rax
1b: 48 29 f8 sub %rdi,%rax
1e: c3 retq
$ echo ��flag{$N}�� > /dev/null先按这里编译出foo.o N随便填一个数即可。
然后用objdump对比汇编。保证大致相同。
根据objdump的结果直接编辑十六进制,将里面的内容完全修改成题目的样子。
然后写个c程序调用div()函数。先用一个大数来除,然后看结果不断缩小范围,最终找到N。

附上代码:
#include <stdio.h> long long div(long long x); int main() { long long result = 0; for (long long i=974873638400001;i<974873638500001;i+=1) { result = div(i); printf("%lld %lld\n",i, result); if (result==1) { printf("%lld\n",i); return 0; } } return 0; }
Misc
bitcoin_base
根据提示,这是bitcon base58
网上随便找个网站decode。比如http://lenschulwitz.com/base58
然后用python decode一下。
>>> import codecs
>>> codecs.decode("666C61677B6231746330696E5F3661736535387D",'hex')
b'flag{b1tc0in_6ase58}'
>>>我最爱吃培根卷
暗示这是培根密码。密文特征看也觉得是。
搜到一个网址 http://rumkin.com/tools/cipher/baconian.php
解决
和尚解密
一开始摸不着头脑,在网上找了下发现这个网址。
与佛论禅
http://www.keyfc.net/bbs/tools/tudoucode.aspx
直接就能解开了。(我还是太年轻,只知道社会主义价值观。)
假的,都是假的
Zip伪加密
用HxD将第48位改为00。 或者找现成的工具也行。比如 ZipCenOp.jar
真的,这次来真的。
AZPR暴力破解。密码居然只是5位数字。
怀旧
提示:这只是一个普通的压缩包
打开发现secret.png的文件头被破坏了。 用强大的101 editor 一看,被修改的地方清晰清晰可见。
把7A改成74 即可解压出文件。
用stegsolve发现是GIF文件。找来找去只发现了半张二维码。
后来发现frame是2。于是保存下来。再使用PhotoShop大法拼成一张。成功。
这好像是一个图片
是个不完整的二维码
用Photoshop拼接回去。
再从别的二维码内切出三个正方形,用手机扫描即可。
(PS:忘了保存成品,只找到个半成品留下来。)

柯哀
Stegsolve 发现a.png 里面blue 通道有盲水印,得知网上有很多工具可解。
遂找工具解决 https://github.com/linyacool/blind-watermark
++__++
下载文件,用16进制编辑器查看发现是数据包。
用wireshark打开。过滤器输入tcp追踪tcp流,发现http请求一个rar压缩包
用python保存。发现压缩包被加密。
rar_file = bytearray([0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x01, 0x00,
0x33, 0x92, 0xb5, 0xe5, 0x0a, 0x01, 0x05, 0x06,
0x00, 0x05, 0x01, 0x01, 0x80, 0x80, 0x00, 0x76,
0xcd, 0x9a, 0xb5, 0x55, 0x02, 0x03, 0x3c, 0xb0,
0x00, 0x04, 0x96, 0x00, 0x20, 0x2c, 0x37, 0x52,
0x5a, 0x80, 0x03, 0x00, 0x08, 0x66, 0x6c, 0x61,
0x67, 0x2e, 0x74, 0x78, 0x74, 0x30, 0x01, 0x00,
0x03, 0x0f, 0x92, 0x2d, 0x6d, 0xd5, 0x6c, 0xf4,
0x0a, 0x07, 0xb5, 0x6f, 0x11, 0x2d, 0xff, 0xca,
0x5b, 0x56, 0x19, 0x2b, 0x06, 0xd3, 0xe8, 0x22,
0xbd, 0xa2, 0xb3, 0xf7, 0xed, 0x99, 0x0b, 0x8a,
0x2e, 0xae, 0x9d, 0xcc, 0xb4, 0x1a, 0xcf, 0xf0,
0x6a, 0x79, 0xa9, 0x4b, 0xc1, 0x3b, 0x0a, 0x03,
0x02, 0x04, 0xfa, 0xd8, 0x28, 0x34, 0x0b, 0xd3,
0x01, 0xa8, 0x63, 0x68, 0x72, 0x51, 0xba, 0x96,
0x49, 0xaa, 0x23, 0x6b, 0xd3, 0x4a, 0x05, 0x1d,
0xd5, 0x7b, 0xc5, 0x03, 0xa2, 0x2a, 0xb2, 0x1a,
0xe8, 0xd8, 0x00, 0x7b, 0x3d, 0x04, 0x15, 0xec,
0x27, 0xbf, 0xa3, 0xb3, 0xde, 0xce, 0xa5, 0xe9,
0x15, 0x8b, 0x8f, 0x10, 0xd2, 0x27, 0xfa, 0x9e,
0x1a, 0x1d, 0x77, 0x56, 0x51, 0x03, 0x05, 0x04,
0x00])
with open("flag.rar",'wb') as file:
file.write(rar_file)查看wireshark以寻找线索。
发现一段与telnet相关的数据,追踪tcp流,发现一个程序和一段base64。
直接用里面提供的程序解密即可。
解密后发现密码。
slots
题目有源码
def line():
return [random.choice(FRUITS) for i in range(3)]
def payout(line):
for fruit in line:
combo = fruit + str(line.count(fruit))
if combo in COMBOS:
return COMBOS[combo]
return 0
# 因为有这一行,玩家永远赢不到一分钱。这个循环保证line2的payout是0
while payout(line2):
line2 = line()try:
#这里传入一个字符串,然后变成float类型
bet = float(bet)
except:
req.sendall('Your bet must be a number!\n')
req.close()
return
if bet <= 0:
req.sendall('Sneaky, but not good enough.\n')
req.close()
return
elif bet > money:
req.sendall('You don\'t have enough money to wager this.\n')
req.close()
return那个float()肯定有问题。随去查文档https://docs.python.org/3/library/functions.html#float
然后发现NaN的运算结果一定是NaN。
输入nan得到flag