CVE-2023-25139

0x00 背景 TPCTF里和qym师傅研究了两天safehttpd这题(虽然我一直在背英语pre,没干什么活),把整个程序可能的漏洞点翻遍了也没找到突破口的off by null如何触发(实际上是测试过程中出现了重大失误,已经找对了地方却没有测试出漏洞),赛后看wp发现有这样一个CVE: 30068 – (CVE-2023-25139) incorrect printf output for integers with thousands separator and width field (CVE-2023-25139) (sourceware.org) 0x01 分析 先看上面链接中的复现样例: #include <stdio.h> #include <locale.h> int main (void) { if (setlocale (LC_ALL, "")) { printf ("1234567890123:\n"); printf ("%0+ -'13ld:\n", 1234567L); } return 0; } 在有漏洞的Glibc2.37下的输出: 1234567890123: +1,234,567 : 输出的长度是15而不是13,因为两个千位分隔符没有被计入宽度,导致输出时多补了两个空格。 这是一个glibc 2.37里短暂出现就被迅速修复的漏洞:千位分隔符在限制长度的格式化输出时没有被正确计入宽度,导致出现了溢出。 由该commit修复: Account for grouping in printf width (bug 30068) · bminor/glibc@c980549 (github.com) 下面就通过这个修复的commit分析一下这个bug是如何产生的。 其中第266行由 width -= workend - string + prec 改成了 width -= number_length + prec_inc 这里的width变量为补足宽度限制需要添加的字符的宽度。prec和prec_inc的值是相同的,区别在于number_length和workend - string并不等同:(168-182行) int number_length; #ifndef COMPILE_WPRINTF if (use_outdigits && base == 10) number_length = __translated_number_width (_NL_CURRENT_LOCALE, string, workend); else number_length = workend - string; if (group) number_length += iter.separators * strlen (thousands_sep); #else number_length = workend - string; /* All wide separators have length 1. */ if (group && thousands_sep != L'\0') number_length += iter.separators; #endif 在上面代码的后几行可以看到number_length是原本的数字长度加上千位分隔符的长度,而workend-string没有计算千位分隔符的长度,导致了错误的长度计算。 ...

December 11, 2023 · 2 min · 311 words · JuicyMio

[BUUCTF]others_shellcode wp

0x00 前置知识 x86_32下应用程序调用系统调用的过程: 把系统调用号存入eax. 把函数参数存在其他寄存器(ebx, ecx, edx, esi, edi), 当系统调用参数大于6个时,全部参数应该依次放在一块连续的内存区域里,同时在 ebx 中保存指向该内存区域的指针. 触发0x80中断, 切换到内核态, 执行中断处理函数. (为了节约宝贵的中断号, Linux用int 0x80触发所有系统调用. 再为每个系统调用分配与中断号用法类似的系统调用号) 0x01 题目分析 IDA看到main函数里只执行了一个getShell()函数, 代码如下. int getShell() { int result; // eax char v1[9]; // [esp-Ch] [ebp-Ch] BYREF strcpy(v1, "/bin//sh"); result = 11; __asm { int 80h; LINUX - sys_execve } return result; } 插入了汇编代码int 0x80. 根据IDA的注释可以看到eax的值就是result的值11. 而32位下系统调用号11即为execve. 32位调用参数存在栈里, 此处即为栈顶的v[1]内的"/bin/sh". 所以相当于手动调用了系统调用execve("/bin/sh").

March 16, 2023 · 1 min · 59 words · JuicyMio

Pwnable.kr

pwnable.kr writeup 1 fd 文件描述符(file descricptor) 维基百科:文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。 习惯上 标准输入(stdin)为0, 标准输出(stdout)为1, 标准错误(stderr)为2. //fd.c #include <stdio.h> #include <stdlib.h> #include <string.h> char buf[32]; int main(int argc, char* argv[], char* envp[]){ if(argc<2){ printf("pass argv[1] a number\n"); return 0; } int fd = atoi( argv[1] ) - 0x1234; int len = 0; len = read(fd, buf, 32); if(!strcmp("LETMEWIN\n", buf)){ printf("good job :)\n"); system("/bin/cat flag"); exit(0); } printf("learn about Linux file IO\n"); return 0; } 得到flag的关键在于如何通过第二个if语句, 也就是如何让buf=LETMEMIN 可以看到read从fd中读入32个字节为buf赋值, 如果我们想控制buf的值就要控制fd中的内容, 那么只要让fd=0, 再向标准输入(从命令行直接输入)中输入LETMEWIN就可以了. 注意到fd在此处赋值: int fd = atoi( argv[1] ) - 0x1234; 其中atoi是将字符串转化为整数的函数 int main(int argc, char* argv[], char* envp[]){ 可以看到argv[]是main函数的一个参数, 而main函数的参数都是程序运行时在命令行输入的, argc代表输入参数的个数(第一个参数是程序路径), argv则存储着指向这些参数的指针. envp存储指向环境变量的指针, 此处没用到. 例如运行程序fd ./fd 4660 此时argc值为2, 而argv[0] = “./fd”, argv[1] = “4660” atoi不能转化16进制数, 所以我们手动转换0x1234, 它的十进制表示是4660 这样我们就可以让fd = 0, 程序等待从stdin中读取字符 我们再向命令行中输入LETMEWIN, 即可得到flag ...

October 12, 2022 · 15 min · 3158 words · JuicyMio