CTCP es uno de esos protocolos que llevan décadas funcionando en silencio y que la mayoría de usuarios de IRC usa sin saber que existe. Cada vez que escribes /me en un canal, estás enviando un mensaje CTCP ACTION. Cada vez que alguien te hace un VERSION y tu cliente responde automáticamente, eso es CTCP. Y cada transferencia DCC que hayas hecho en tu vida pasó por una negociación CTCP.
Llevo operando servidores IRC desde 1997 y CTCP ha sido tanto una herramienta útil como un vector de problemas. Este artículo va a explicar el protocolo desde el nivel de bytes hasta las implicaciones prácticas de seguridad.
¿Qué es CTCP y por qué existe?
CTCP significa Client-To-Client Protocol. El nombre es un poco engañoso porque los mensajes pasan por el servidor IRC — no son realmente "cliente a cliente" en el sentido de una conexión directa (eso sería DCC). Lo que CTCP hace es incrustar comandos especiales dentro de mensajes PRIVMSG y NOTICE normales, usando un carácter delimitador especial.
¿Por qué se diseñó así? Porque el protocolo IRC original (RFC 1459) no tenía un mecanismo para comunicación estructurada entre clientes. Los mensajes eran texto plano, punto. CTCP fue un hack elegante: usar un carácter que no aparece en texto normal como delimitador, y meter comandos ahí dentro. El servidor no necesita saber nada — simplemente reenvía el PRIVMSG como haría con cualquier otro.
El carácter delimitador es \x01 (SOH, Start of Heading, ASCII 1). Cualquier texto entre dos \x01 dentro de un PRIVMSG se interpreta como CTCP.
Anatomía de un mensaje CTCP
Un mensaje CTCP tiene esta estructura a nivel de protocolo IRC:
# Petición CTCP (se envía como PRIVMSG):
:nick!user@host PRIVMSG target :\x01COMANDO parámetros\x01
# Respuesta CTCP (se envía como NOTICE):
:nick!user@host NOTICE target :\x01COMANDO respuesta\x01
# Ejemplo real - petición VERSION:
:marco!marco@irc6.net PRIVMSG juan :\x01VERSION\x01
# Respuesta VERSION:
:juan!juan@libera.chat NOTICE marco :\x01VERSION HexChat 2.16.1 / Linux 6.1.0\x01
# Ejemplo ACTION (lo que envía /me):
:marco!marco@irc6.net PRIVMSG #canal :\x01ACTION está compilando UnrealIRCd por enésima vez\x01
Fíjate en la diferencia: las peticiones van como PRIVMSG, las respuestas como NOTICE. Esto es una regla que todo cliente debería respetar. Si un cliente responde con PRIVMSG en vez de NOTICE, puede generar bucles infinitos entre dos clientes que se responden mutuamente.
Los comandos CTCP estándar
VERSION
El más conocido. El cliente que recibe la petición responde con el nombre y versión de su software. Es útil para estadísticas de red y para detectar clientes problemáticos o vulnerables.
# Enviar petición VERSION a un usuario:
/ctcp nick VERSION
# Respuestas típicas:
VERSION WeeChat 4.1.2 (Linux; x86_64)
VERSION HexChat 2.16.1 / Linux 6.1.0-amd64
VERSION irssi v1.4.4
VERSION KVIrc 5.0.0 Aria
Como operador, he usado VERSION scans masivos para detectar botnets. Cuando ves 200 conexiones nuevas que responden todas con la misma cadena VERSION sospechosa, tienes un patrón claro para un kline.
PING
Envía un timestamp y espera que el otro cliente lo devuelva. La diferencia de tiempo te da la latencia aproximada entre ambos clientes (a través del servidor).
# Enviar CTCP PING:
/ctcp nick PING
# A nivel de protocolo:
PRIVMSG nick :\x01PING 1686230400\x01
# Respuesta:
NOTICE marco :\x01PING 1686230400\x01
# Tu cliente calcula: tiempo_actual - 1686230400 = latencia
TIME
Pide la hora local del otro usuario. Parece inofensivo, pero revela la zona horaria del usuario, lo cual puede ser información más sensible de lo que parece.
# Petición:
/ctcp nick TIME
# Respuesta típica:
TIME Thu Jun 08 15:32:10 2023 CEST
ACTION
El comando detrás de /me. A diferencia de los otros, ACTION no espera respuesta. Es unidireccional.
# Cuando escribes:
/me compila código
# Se envía:
PRIVMSG #canal :\x01ACTION compila código\x01
# El cliente receptor lo muestra como:
* marco compila código
SOURCE, USERINFO, CLIENTINFO
Comandos menos usados. SOURCE indica dónde obtener el código fuente del cliente. USERINFO devuelve una cadena configurable por el usuario. CLIENTINFO lista qué comandos CTCP soporta el cliente. En la práctica, pocos clientes modernos implementan los tres.
DCC: conexiones directas vía CTCP
DCC (Direct Client-to-Client) usa CTCP para la negociación inicial, pero después establece una conexión TCP directa entre los dos clientes, sin pasar por el servidor IRC. Esto se diseñó para transferencia de archivos y para chat privado que no pudiera ser interceptado por el servidor.
# Negociación DCC SEND (transferencia de archivo):
# El emisor envía:
PRIVMSG nick :\x01DCC SEND archivo.tar.gz 3232235777 5000 1048576\x01
# ^IP decimal ^puerto ^tamaño
# La IP 3232235777 = 192.168.1.1 en formato decimal
# El receptor abre una conexión TCP a esa IP:puerto
# DCC CHAT (chat directo):
PRIVMSG nick :\x01DCC CHAT chat 3232235777 5001\x01
El problema gordo con DCC: expone la IP real del emisor. No hay forma de evitarlo en el protocolo original — es el diseño mismo. Si estás detrás de un cloak o usas Tor, un DCC SEND revela tu IP real al receptor. Esto ha sido explotado innumerables veces.
Algunos servidores modernos implementan DCC passthrough o DCC relay para mitigar esto, pero no es universal.
/set irc.ctcp.dcc "". Reduces tu superficie de ataque sin perder nada que no puedas hacer con un servicio de transferencia externo.CTCP y seguridad: los riesgos reales
CTCP tiene varios vectores de ataque que he visto explotar en producción:
1. CTCP flood
El ataque más básico. Un atacante envía cientos de peticiones CTCP VERSION, PING, etc. al objetivo. Si el cliente responde a todas, genera flood de salida y el servidor lo desconecta por excess flood.
# Protección en el lado cliente (WeeChat):
/set irc.ctcp.version ""
/set irc.ctcp.ping ""
/set irc.ctcp.time ""
# Esto silencia las respuestas CTCP completamente
# Protección parcial (responder pero limitar):
# La mayoría de clientes modernos tienen rate-limiting CTCP integrado
# HexChat: 5 respuestas cada 30 segundos por defecto
2. Fingerprinting
La respuesta VERSION revela tu sistema operativo, versión del cliente y a veces la arquitectura. Un atacante puede usar esto para seleccionar exploits específicos.
# Personalizar la respuesta VERSION en WeeChat:
/set irc.ctcp.version "un cliente IRC"
# En HexChat (via plugin Python):
import hexchat
def version_hook(word, word_eol, userdata):
hexchat.command("NCTCP {} VERSION {}".format(word[0], "IRC client"))
return hexchat.EAT_ALL
hexchat.hook_server("PRIVMSG", version_hook)
3. IP leak vía DCC
Ya lo mencioné, pero vale la pena repetirlo: DCC expone tu IP real. Un atacante ni siquiera necesita que aceptes la transferencia — la IP ya está en el mensaje de negociación CTCP.
4. Exploits en parsing CTCP
Históricamente ha habido vulnerabilidades en cómo los clientes parsean mensajes CTCP. Cadenas malformadas con caracteres de control anidados han causado crashes, buffer overflows y en casos extremos ejecución de código. Los clientes modernos son más resistentes, pero el riesgo existe en implementaciones antiguas.
Configuración del lado servidor
Como operador de red, tienes herramientas para gestionar el abuso CTCP a nivel de servidor:
UnrealIRCd
# unrealircd.conf - Limitar CTCP flood
set {
anti-flood {
target-flood {
# Máximo 4 CTCPs por 30 segundos a un usuario
private-ctcp 4:30;
# Máximo 10 CTCPs por 15 segundos en un canal
channel-ctcp 10:15;
}
}
}
# User mode +T bloquea CTCPs entrantes
# Channel mode +C bloquea CTCPs en el canal
InspIRCd
# inspircd.conf
# Permite channel mode +C para bloquear CTCP
# User mode +T para bloquear CTCP privados
# Limitar CTCP con el módulo de anti-flood:
Políticas de red
En redes que he administrado, la política estándar es:
- CTCP ACTION permitido siempre (es funcionalidad básica de chat)
- CTCP VERSION, PING, TIME permitidos con rate-limiting estricto
- CTCP DCC bloqueado por defecto en canales, permitido en privado
- CTCP desconocidos (custom) bloqueados completamente
Esta configuración cubre el 99% de los casos de uso legítimos y bloquea la mayoría de abusos.
Preguntas frecuentes
¿CTCP funciona a través de bouncers como ZNC?
Sí, pero con matices. ZNC reenvía peticiones CTCP al cliente conectado. Si no hay cliente conectado, ZNC puede responder por ti con una cadena configurable. El problema es la latencia: si tu bouncer está en Frankfurt y tú en Buenos Aires, un CTCP PING va a mostrar la latencia bouncer-peticionario, no la tuya real. Puedes configurar ZNC para bloquear CTCP con el módulo ctcpflood.
¿Es posible enviar CTCP a un canal entero?
Técnicamente sí — envías un PRIVMSG con delimitadores CTCP al canal en vez de a un nick. En la práctica, la mayoría de servidores modernos bloquean CTCP a canales (excepto ACTION) o lo limitan severamente. Un CTCP VERSION a un canal de 500 personas generaría 500 respuestas NOTICE simultáneas, lo cual es básicamente un ataque DDoS autoinfligido.
¿CTCP tiene futuro con IRCv3?
IRCv3 no elimina CTCP pero reduce la necesidad de algunos comandos. Por ejemplo, message-tags permite adjuntar metadatos a mensajes sin recurrir a CTCP. server-time proporciona timestamps del servidor, haciendo TIME menos relevante. ACTION probablemente seguirá indefinidamente porque es demasiado ubicuo como para cambiarlo. El draft de CTCP en IRCv3 básicamente estandariza lo que ya existe y desaconseja los comandos más oscuros.
¿Puedo crear mis propios comandos CTCP?
Sí, es trivial. El protocolo no define un conjunto cerrado de comandos. Si tu cliente envía \x01MICOMANDO datos\x01 dentro de un PRIVMSG, es un CTCP válido. El cliente receptor necesita saber qué hacer con él — si no lo reconoce, simplemente lo ignora. Algunos bots usan CTCP custom para comunicación entre nodos.