1. 获取加密信息及随机数

    参数:

    {
    	"jsonrpc": "2.0",
    	"id": 0,
    	"method": "challenge",
    	"params": {
    		"username": "admin"
    	}
    }
    

    响应

    {
    	"jsonrpc": "2.0",
    	"id": 0,
    	"result": {
    		"nonce": "b5f3fae1f6f5ac540419e62cfc844102",
    		"alg": 6,
    		"salt": "onk/l6Jr"
    	}
    }
    

    alg:当前用户的密码所用的加密算法

    1: MD5

    5: SHA256

    6: SHA512

    salt:一个字符串,参与 Linux 系统用户密码的计算

    nonce: 随机字符串,只有 1000ms 的生命期

  2. 生成 Linux 系统用户密码

    pw=$(openssl passwd -$alg -salt "$salt" "$password")
    

    JS 用法:https://www.npmjs.com/package/unixpass

    const up = require('unixpass');
    up.crypt('mypassword', '$6$onk/l6Jr$');
    

    得到的 pw 形如:

    $6$onk/l6Jr$8OuHAcY4lF3nC/XfOm3MhCxLG8eADL8RTDcNDXTIe9fEhSuGl1w2VkmWbFFy0ZUEi9ar89ZtPIYucOAJ30lWb/
    
  3. 生成用于登录的 hash 值

    对 username:pw:nonce 进行 MD5 校验

    hash=$(echo -n "$username:$pw:$nonce" | md5sum | cut -d' ' -f1)
    
  4. 调用登录接口

    参数:

    {
    	"jsonrpc": "2.0",
    	"id": 0,
    	"method": "login",
    	"params": {
    		"username": "admin",
    		"hash": "b5f3fae106f5ac540419e62cfc844100"
    	}
    }
    

    响应:

    {
    	"jsonrpc": "2.0",
    	"id": 0,
    	"result": {
    		"sid": "b9e1650812289489a5b6c57d8af09ab9",
    		"username": "admin"
    	}
    }
    
  5. shell脚本获取sid示例

    #!/bin/sh
    
    host=$1
    username=$2
    password=$3
    
    ret=$(curl -k <http://$host/rpc> -d "{\\"method\\":\\"challenge\\",\\"params\\":{\\"username\\":\\"$username\\"}}")
    salt="$(echo $ret|awk -F "salt" '{print $2}'|cut -d '"' -f 3)"
    nonce="$(echo $ret|awk -F "nonce" '{print $2}'|cut -d '"' -f 3)"
    alg="$(echo $ret|awk -F "alg" '{print $2}'|awk -F ":|," '{print $2}')"
    
    pw=$(openssl passwd -$alg -salt "$salt" "$password")
    
    hash=$(echo -n "$username:$pw:$nonce" | md5sum | cut -d' ' -f1)
    
    echo $hash
    
    curl -k <http://$host/rpc> -d "{\\"method\\":\\"login\\",\\"params\\":{\\"username\\":\\"root\\",\\"hash\\":\\"$hash\\"}}"
    
  6. C 语言获取

#include <openssl/ssl.h>
#include <curl/curl.h>
#include <jansson.h>
#include <string.h>
#include <stdint.h>
#include <stdio.h>
#include <crypt.h>

#if OPENSSL_VERSION_MAJOR < 3
#include <openssl/md5.h>
#endif

struct curl_resp_data {
    size_t size;
    char *data;
};

static size_t curl_write_data(void *buffer, size_t size, size_t nmemb, void *userp)
{
    struct curl_resp_data *resp = userp;
    size_t realsize = size * nmemb;

    char *data = realloc(resp->data, resp->size + realsize + 1);
    if (!data)
        return 0;

    memcpy(data + resp->size, buffer, realsize);
    resp->size += realsize;
    resp->data = data;

    data[resp->size] = '\\0';

    return realsize;
}

static json_t *call_challenge(CURL *curl, const char *username)
{
    struct curl_resp_data resp = {};
    json_t *data, *result = NULL;
    char *body;

    data = json_pack("{s:s,s:o}", "method", "challenge",
        "params", json_pack("{s:s}", "username", username));

    body = json_dumps(data, 0);

    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &resp);
    curl_easy_perform(curl);

    free(body);

    data = json_loads(resp.data, 0, NULL);

    free(resp.data);

    json_unpack(data, "{s:O}", "result", &result);

    json_decref(data);

    return result;
}

static json_t *call_login(CURL *curl, const char *username, const char *hash)
{
    struct curl_resp_data resp = {};
    json_t *data, *result = NULL;
    char *body;

    data = json_pack("{s:s,s:o}", "method", "login",
        "params", json_pack("{s:s,s:s}", "username", username, "hash", hash));

    body = json_dumps(data, 0);

    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &resp);
    curl_easy_perform(curl);

    free(body);

    data = json_loads(resp.data, 0, NULL);

    free(resp.data);

    json_unpack(data, "{s:O}", "result", &result);

    json_decref(data);

    return result;
}

int main(int argc, char const *argv[])
{
    const char *url = "<http://192.168.8.1/rpc>";
    const char *username = "root";
    const char *password = "1";
    const char *salt, *nonce, *pw, *sid;
    uint8_t md5_result[16];
    int alg;
    char setting[256], hash[33] = "";
#if OPENSSL_VERSION_MAJOR < 3
    MD5_CTX ctx;
#else
    EVP_MD_CTX *ctx;
    unsigned int md5len;
#endif
    json_t *result;
    int i;
    CURL *curl;

    curl = curl_easy_init();
    if (!curl) {
        fprintf(stderr, "curl_easy_init fail\\n");
        return -1;
    }

    curl_easy_setopt(curl, CURLOPT_URL, url);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_data);

    result = call_challenge(curl, username);
    if (!result) {
        fprintf(stderr, "invalid username\\n");
        curl_easy_cleanup(curl);
        return -1;
    }

    json_unpack(result, "{s:i,s:s,s:s}", "alg", &alg, "salt", &salt, "nonce", &nonce);

    sprintf(setting, "$%d$%s$", alg, salt);
    pw = crypt(password, setting);

#if OPENSSL_VERSION_MAJOR < 3
    MD5_Init(&ctx);
    MD5_Update(&ctx, username, strlen(username));
    MD5_Update(&ctx, ":", 1);
    MD5_Update(&ctx, pw, strlen(pw));
    MD5_Update(&ctx, ":", 1);
    MD5_Update(&ctx, nonce, strlen(nonce));
    MD5_Final(md5_result, &ctx);
#else
    ctx = EVP_MD_CTX_new();
    EVP_DigestInit_ex(ctx, EVP_md5(), NULL);
    EVP_DigestUpdate(ctx, username, strlen(username));
    EVP_DigestUpdate(ctx, ":", 1);
    EVP_DigestUpdate(ctx, pw, strlen(pw));
    EVP_DigestUpdate(ctx, ":", 1);
    EVP_DigestUpdate(ctx, nonce, strlen(nonce));
    EVP_DigestFinal(ctx, md5_result, &md5len);
    EVP_MD_CTX_free(ctx);
#endif

    json_decref(result);

    for (i = 0; i < 16; i++)
        sprintf(&hash[i * 2], "%02x", md5_result[i]);

    result = call_login(curl, username, hash);
    if (!result) {
        fprintf(stderr, "invalid password\\n");
        curl_easy_cleanup(curl);
        return -1;
    }

    json_unpack(result, "{s:s}", "sid", &sid);
    printf("sid: %s\\n", sid);

    json_decref(result);

    curl_easy_cleanup(curl);

    return 0;
}

C语言示例编译命令

sudo apt install libssl-dev libcurl4-openssl-dev libjansson-dev
gcc test.c -lcurl -ljansson -lcrypt -lcrypto